Based on the question convert from float-point to custom numeric type, I figured out a portable safe way to convert float-point type into array of integers and the code works fine, but for some values when converting from double to unsigned long long with precision that can be safely represented by unsigned long long the conversion fails not by compile-time error but with invalid value which is minimum representable value for signed long long or zero, the conversion fails on visual c++ 2008, intel xe 2013 and gcc 4.7.2.
here is the code: (notice first statement inside while loop in main function)
#ifndef CHAR_BIT
#include <limits.h>
#endif
#include <float.h>
#include <math.h>
typedef signed int int32;
typedef signed long long int64;
typedef unsigned int uint32;
typedef unsigned long long uint64;
typedef float float32;
typedef double float64;
// get size of type in bits corresponding to CHAR_BIT.
template<typename t>
struct sizeof_ex
{
static const uint32 value = sizeof(t) * CHAR_BIT;
};
// factorial function
float64 fct(int32 i)
{
float64 r = 1;
do r *= i; while(--i > 1);
return r;
}
int main()
{
// maximum 2 to power that can be stored in uint32
const uint32 power_2 = uint32(~0);
// number of binary digits in power_2
const uint32 digit_cnt = sizeof_ex<uint32>::value;
// number of array elements that will store expanded value
const uint32 comp_count = DBL_MAX_EXP / digit_cnt + uint32((DBL_MAX_EXP / digit_cnt) * digit_cnt < DBL_MAX_EXP);
// array elements
uint32 value[comp_count];
// get factorial for 23
float64 f = fct<float64>(23);
// save sign for later correction
bool sign = f < 0;
// remove sign from float-point if exists
if (sign) f *= -1;
// get number of binary digits in f
uint32 actual_digits = 0;
frexp(f, (int32*)&actual_digits);
// get start index in array for little-endian format
uint32 start_index = (actual_digits / digit_cnt) + uint32((actual_digits / digit_cnt) * digit_cnt < actual_digits) - 1;
// get all parts but the last
while (start_index > 0)
{
// store current part
// in this line the compiler fails
value[start_index] = uint64(f / power_2);
// exclude it from f
f -= power_2 * float64(value[start_index]);
// decrement index
--start_index;
}
// get last part
value[0] = uint32(f);
}
The convert code above will give different result from compiler to another, meaning when the parameter of factorial function say 20 all compilers return valid result, when the value greater than 20 some compiler gets part of the result others don't and when it is get bigger e.g. 35 it become zero.
please tell me why those error occurs?
thank you.
I don't think your conversion logic makes any sense.
You have a value called "power_2" which is not actually a power of 2, despite commenting that it is.
You extract bits of a very large (>64-bit) number by dividing by something less than 32-bits. Obviously the result of that will be >32 bits, but you store it into a 32-bit value, truncating it. Then you remultiply that by the original divisor and subtract from your float. However as the number was truncated, you are subtracting much less than the original value, which almost certainly wasn't what you expected.
I think there's more wrong that just that - you don't really always want the top 32 bits, for a number which is not a multiple of 32-bits long, you want the actual length mod 32.
Here's a somewhat lazy hack on your code that does what I think you're trying to do. Note that the pow() could be optimised out.
while (start_index > 0)
{
float64 fpow = pow(2., 32. * start_index);
// store current part
// in this line the compiler fails
value[start_index] = f / fpow;
// exclude it from f
f -= fpow * float64(value[start_index]);
// decrement index
--start_index;
}
That's pretty much untested, but hopefully illustrates what I mean.
Related
Let's assume we have a representation of -63 as signed seven-bit integer within a uint16_t. How can we convert that number to float and back again, when we don't know the representation type (like two's complement).
An application for such an encoding could be that several numbers are stored in one int16_t. The bit-count could be known for each number and the data is read/written from a third-party library (see for example the encoding format of tivxDmpacDofNode() here: https://software-dl.ti.com/jacinto7/esd/processor-sdk-rtos-jacinto7/latest/exports/docs/tiovx/docs/user_guide/group__group__vision__function__dmpac__dof.html --- but this is just an example). An algorithm should be developed that makes the compiler create the right encoding/decoding independent from the actual representation type. Of course it is assumed that the compiler uses the same representation type as the library does.
One way that seems to work well, is to shift the bits such that their sign bit coincides with the sign bit of an int16_t and let the compiler do the rest. Of course this makes an appropriate multiplication or division necessary.
Please see this example:
#include <iostream>
#include <cmath>
int main()
{
// -63 as signed seven-bits representation
uint16_t data = 0b1000001;
// Shift 9 bits to the left
int16_t correct_sign_data = static_cast<int16_t>(data << 9);
float f = static_cast<float>(correct_sign_data);
// Undo effect of shifting
f /= pow(2, 9);
std::cout << f << std::endl;
// Now back to signed bits
f *= pow(2, 9);
uint16_t bits = static_cast<uint16_t>(static_cast<int16_t>(f)) >> 9;
std::cout << "Equals: " << (data == bits) << std::endl;
return 0;
}
I have two questions:
This example uses actually a number with known representation type (two's complement) converted by https://www.exploringbinary.com/twos-complement-converter/. Is the bit-shifting still independent from that and would it work also for other representation types?
Is this the canonical and/or most elegant way to do it?
Clarification:
I know the bit width of the integers I would like to convert (please check the link to the TIOVX example above), but the integer representation type is not specified.
The intention is to write code that can be recompiled without changes on a system with another integer representation type and still correctly converts from int to float and/or back.
My claim is that the example source code above does exactly that (except that the example input data is hardcoded and it would have to be different if the integer representation type were not two's complement). Am I right? Could such a "portable" solution be written also with a different (more elegant/canonical) technique?
Your question is ambiguous as to whether you intend to truly store odd-bit integers, or odd-bit floats represented by custom-encoded odd-bit integers. I'm assuming by "not knowing" the bit-width of the integer, that you mean that the bit-width isn't known at compile time, but is discovered at runtime as your custom values are parsed from a file, for example.
Edit by author of original post:
The assumption in the original question that the presented code is independent from the actual integer representation type, is wrong (as explained in the comments). Integer types are not specified, for example it is not clear that the leftmost bit is the sign bit. Therefore the presented code also contains assumptions, they are just different (and most probably worse) than the assumption "integer representation type is two's complement".
Here's a simple example of storing an odd-bit integer. I provide a simple struct that let's you decide how many bits are in your integer. However, for simplicity in this example, I used uint8_t which has a maximum of 8-bits obviously. There are several different assumptions and simplifications made here, so if you want help on any specific nuance, please specify more in the comments and I will edit this answer.
One key detail is to properly mask off your n-bit integer after performing 2's complement conversions.
Also please note that I have basically ignored overflow concerns and bit-width switching concerns that may or may not be a problem depending on how you intend to use your custom-width integers and the maximum bit-width you intend to support.
#include <iostream>
#include <string>
struct CustomInt {
int bitCount = 7;
uint8_t value;
uint8_t mask = 0;
CustomInt(int _bitCount, uint8_t _value) {
bitCount = _bitCount;
value = _value;
mask = 0;
for (int i = 0; i < bitCount; ++i) {
mask |= (1 << i);
}
}
bool isNegative() {
return (value >> (bitCount - 1)) & 1;
}
int toInt() {
bool negative = isNegative();
uint8_t tempVal = value;
if (negative) {
tempVal = ((~tempVal) + 1) & mask;
}
int ret = tempVal;
return negative ? -ret : ret;
}
float toFloat() {
return toInt(); //Implied truncation!
}
void setFromFloat(float f) {
int intVal = f; //Implied truncation!
bool negative = f < 0;
if (negative) {
intVal = -intVal;
}
value = intVal;
if (negative) {
value = ((~value) + 1) & mask;
}
}
};
int main() {
CustomInt test(7, 0b01001110); // -50. Would be 78 if this were a normal 8-bit integer
std::cout << test.toFloat() << std::endl;
}
I am using zlib 1.2.11. When i use gzseek with offset greater than the range of a signed int (half of 2^32) ; I get bytes_read as -1. Is there any way to use 64 bit offset.
Here is an excerpt from the definition of gzseek64 from gzlib.c
if (state->mode == GZ_READ) {
n = GT_OFF(state->x.have) || (z_off64_t)state->x.have > offset ?(unsigned)offset : state->x.have;
state->x.have -= n;
state->x.next += n;
state->x.pos += n;
offset -= n;
}
here n is unsigned int which can hold only 32 bits...even if my offset was 64 bits how would n hold that value.... Please correct me if i am wrong
More Findings:
After declaring the macro Z_LARGE64 ... gzseek accepts 64 bit offset but ...
When i pass a large offset the bytes seeked returned is a large negative value and i think it has to do with the __int64 to unsigned int conversion
Compile the library with Z_LARGE64 and then use that lib to build your application. In the application include Z_LARGE64 before #include "zlib.h". Then use gzseek64()
I need to get the lowest 31 bits of some integer, that is bigger than standard 32 bit int.
Either getting an 32 bit int with the lowest 31 bits of the large integer, or getting a filled bytearray from the large integer will suffice.
In c# I would use BigInt and .toByteArray - is there something similar in c++11 (I am a noob in c++)?
Mask the lowest 31 bits and return the result:
template<typename T>
T mask31(T x) {
static_assert(std::is_integral<T>::value, "mask31 is only supported on integers!");
return x & (T)0x7fffffff;
}
If you know the types you are working with, you can of course do away with the template goop and just mask directly inline :-)
Assuming you have the value in a long variable, you can get the 31 bits you want in a 4 byte array with a code like this example:
#include <stdint.h>
#include <stdio.h>
int main(){
long int lvalue = 0x1234567890abcdefL;
unsigned char byteArray[4];
*(int32_t *)byteArray = (int32_t)(lvalue & 0x7fffffffL); // 0x10abcdef
for(int i=0; i<4; i++)
printf("Byte[%d] = %02X\n", i, byteArray[i]);
return 0;
}
Of course that the order of bytes will depend on your system.
I've been stumped on this one for days. I've written this program from a book called Write Great Code Volume 1 Understanding the Machine Chapter four.
The project is to do Floating Point operations in C++. I plan to implement the other operations in C++ on my own; the book uses HLA (High Level Assembly) in the project for other operations like multiplication and division.
I wanted to display the exponent and other field values after they've been extracted from the FP number; for debugging. Yet I have a problem: when I look at these values in memory they are not what I think they should be. Key words: what I think. I believe I understand the IEEE FP format; its fairly simple and I understand all I've read so far in the book.
The big problem is why the Rexponent variable seems to be almost unpredictable; in this example with the given values its 5. Why is that? By my guess it should be two. Two because the decimal point is two digits right of the implied one.
I've commented the actual values that are produced in the program in to the code so you don't have to run the program to get a sense of whats happening (at least in the important parts).
It is unfinished at this point. The entire project has not been created on my computer yet.
Here is the code (quoted from the file which I copied from the book and then modified):
#include<iostream>
typedef long unsigned real; //typedef our long unsigned ints in to the label "real" so we don't confuse it with other datatypes.
using namespace std; //Just so I don't have to type out std::cout any more!
#define asreal(x) (*((float *) &x)) //Cast the address of X as a float pointer as a pointer. So we don't let the compiler truncate our FP values when being converted.
inline int extractExponent(real from) {
return ((from >> 23) & 0xFF) - 127; //Shift right 23 bits; & with eight ones (0xFF == 1111_1111 ) and make bias with the value by subtracting all ones from it.
}
void fpadd ( real left, real right, real *dest) {
//Left operand field containers
long unsigned int Lexponent = 0;
long unsigned Lmantissa = 0;
int Lsign = 0;
//RIGHT operand field containers
long unsigned int Rexponent = 0;
long unsigned Rmantissa = 0;
int Rsign = 0;
//Resulting operand field containers
long int Dexponent = 0;
long unsigned Dmantissa = 0;
int Dsign = 0;
std::cout << "Size of datatype: long unsigned int is: " << sizeof(long unsigned int); //For debugging
//Properly initialize the above variable's:
//Left
Lexponent = extractExponent(left); //Zero. This value is NOT a flat zero when displayed because we subtract 127 from the exponent after extracting it! //Value is: 0xffffff81
Lmantissa = extractMantissa (left); //Zero. We don't do anything to this number except add a whole number one to it. //Value is: 0x00000000
Lsign = extractSign(left); //Simple.
//Right
**Rexponent = extractExponent(right); //Value is: 0x00000005 <-- why???**
Rmantissa = extractMantissa (right);
Rsign = extractSign(right);
}
int main (int argc, char *argv[]) {
real a, b, c;
asreal(a) = -0.0;
asreal(b) = 45.67;
fpadd(a,b, &c);
printf("Sum of A and B is: %f", c);
std::cin >> a;
return 0;
}
Help would be much appreciated; I'm several days in to this project and very frustrated!
in this example with the given values its 5. Why is that?
The floating point number 45.67 is internally represented as
2^5 * 1.0110110101011100001010001111010111000010100011110110
which actually represents the number
45.6700000000000017053025658242404460906982421875
This is as close as you can get to 45.67 inside float.
If all you are interested in is the exponent of a number, simply compute its base 2 logarithm and round down. Since 45.67 is between 32 (2^5) and 64 (2^6), the exponent is 5.
Computers use binary representation for all numbers. Hence, the exponent is for base two, not base ten. int(log2(45.67)) = 5.
What's the best method for returning an unsigned long from a vector of ints? I'm working on a BigInt class in c++ and I'm storing the large numbers in a vector. I want to write a method that will return this vector as a standard long, provided it isn't larger than unsigned long can hold. Thanks
Something along these lines, assuming the ints are stored in the vector with the least significant first:
size_t bits_in_int = std::numeric_limits<int>::digits;
size_t bits_in_ulong = std::numeric_limits<unsigned long>::digits;
unsigned long accumulator = 0;
size_t bits_so_far = 0;
for (unsigned long i : the_ints) {
size_t next_bits = bits_so_far + bits_in_int;
if (next_bits > bits_in_long) { /* failed, do something about it */}
accumulator += (i << bits_so_far);
bits_so_far = next_bits;
}
return accumulator;
Notes:
1) In practice you could save some bother because the number of loops is going to be either 1 or 2 on any vaguely normal-looking C++ implementation. So you could just write a case where you return the_ints[0] and a case where you return the_ints[0] + (the_ints[1] << bits_in_int).
2) I've been lazy. Because int is signed and unsigned long is unsigned, you can actually fit at least one int plus the least significant bit of another int into an unsigned long. For example you might find bits_in_int is 31 but bits_in_long is 32.
So actually in the "failed" case there is one last hope for peace, which is that (a) there is only one int left to process, and (b) its value fits in the remaining bits of the result. But like I say, I'm lazy, and I think I've shown the components you need to put together.
For this reason if no other, you should probably use a vector of unsigned int for your BigInt. It's not required that the width of unsigned long is a multiple of the number of bits in unsigned int, but it might be strange enough that you can ignore it.
Update for base 10 digits, stored most significant first:
if (the_ints.size() <= std::numeric_limits<unsigned long>::digits10 + 1) {
std::stringstream ss;
for (int i : the_ints) ss << char(i + '0');
unsigned long result;
if (ss >> result) return result;
}
/* failed, do something about it */