Why are float numbers rounded up in Cassandra? - cassandra

I created Cassandra table with column type: DataType.FLOAT.
Execute my SQL using CqlSession:
CqlSessionBuilder builder = CqlSession.builder();
builder.addContactPoint(new InetSocketAddress(properties.getHost(), properties.getPort()));
builder.withLocalDatacenter(properties.getDatacenter());
builder.withAuthCredentials(properties.getUsername(), properties.getPassword());
builder.build();
But when I insert float numbers, it's rounded up:
12334.9999 -> 12335.0.
0.999999 -> 0.999999
12345.9999 -> 12346.0
It seems like Cassandra rounds the float and consider the number of all digits, not only after the point.
What are the options to solve this problem? I know that I can use Decimal datatype, but may be you have other solution?

I actually covered this issue with Apache Cassandra and DataStax Astra DB in an article I wrote last month:
The Guerilla Guide to Building E-commerce Product Services with DataStax Astra DB
So the problem here, is that FLOAT is a fixed floating point precision type. This means that when the numeric values are converted from base-10 (decimal) to base-2 (binary), each one of the 32 binary precision points must have a value (zero or one, obviously). It's during this conversion process between base-2 and base-10 that rounding errors occur. The likelihood of a rounding error increases as the value does (on either side of the decimal point).
What are the options to solve this problem? I know that I can use Decimal datatype, but may be you have other solution?
Well, you mentioned the best solution (IMO), which to use a DECIMAL to store the value. This works, because DECIMAL is an arbitrary floating point type. The values in a DECIMAL type are stored in base-10, so there's no conversion necessary and only the required precision is used.
Before arbitrary precision types came along, we used to use INTEGERs for things that had to be accurate. The first E-commerce team I worked on stored product prices in the DB as pennies, to prevent the rounding issue.
Yes, both INT and FLOAT are fixed precision types, but an INT stores whole numbers, and all of its precision points can be used for that. Therefore the usage patterns of the bits are quite different. While both INT and FLOAT allocate a bit for the "sign" (+/-), with floating point numbers the remaining 31 precision points are pre-allocated for the full numeric value and its exponent.
So your example of 12334.9999 is essentially stored in Cassandra like this:
123349999 x 10^-4
And of course, that's stored in binary, which I won't include here for brevity.
tl;dr;
Basically FLOATs use fixed precision to store values as a formula (significand and exponent) in base-2, and the conversion back to base-10 makes rounding errors likely.
You're right, use a DECIMAL type. When you need to be exact, that's the only real solution.
If you're interested, here are two additional SO answers which provide more detail on this topic:
Double vs. BigDecimal?
What is the difference between the float and integer data type when the size is the same?

Related

Does default memory allocated for a datatype play a role in rounding? In what manner a float is rounded if it exceeds allocated memory?

Having a file test2.py with the following contents:
print(2.0000000000000003)
print(2.0000000000000002)
I get this output:
$ python3 test2.py
2.0000000000000004
2.0
I thought lack of memory allocated for float might be causing this but 2.0000000000000003 and 2.0000000000000002 need same amount of memory.
IEEE 754 64-bit binary floating point always uses 64 bits to store a number. It can exactly represent a finite subset of the binary fractions. Looking only at the normal numbers, if N is a power of two in its range, it can represent a number of the form, in binary, 1.s*N where s is a string of 52 zeros and ones.
All the 32 bit binary integers, including 2, are exactly representable.
The smallest exactly representable number greater than 2 is 2.000000000000000444089209850062616169452667236328125. It is twice the binary fraction 1.0000000000000000000000000000000000000000000000000001.
2.0000000000000003 is closer to 2.000000000000000444089209850062616169452667236328125 than to 2, so it rounds up and prints as 2.0000000000000004.
2.0000000000000002 is closer to 2.0, so it rounds down to 2.0.
To store numbers between 2.0 and 2.000000000000000444089209850062616169452667236328125 would require a different floating point format likely to take more than 64 bits for each number.
Floats are not stored as integers are, with each bit signaling a yes/no term of 1,2,4,8,16,32,... value that you add up to get the complete number. They are stored as sign + mantissa + exponent in base 2. Several combinations have special meaning (NaN, +-inf, -0,...). Positive and negative numbers are idential in mantissa and exponent, only the sign differs.
At any given time they have a specific bit-length they are "put into". They can not overflow.
They have however a minimal accuracy, if you try to fit numbers into them that would need a bigger accuracy you get rounding errors - thats what you see in your example.
More on floats and storage (with example):
http://stupidpythonideas.blogspot.de/2015/01/ieee-floats-and-python.html
(which links to a more technical https://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html)
More on accuracy of floats:
- Floating Point Arithmetic: Issues and Limitations

How to convert string having scientific notation to a float without loosing precision?

I have a string that I need to convert to a float for me to work on.
num = "2.769999999999999574e+00"
If I do a float operation, the precision is gone
print(float(num)) # 2.7699999999999996
If I use a Decimal class ,even then the precision is gone
from decimal import Decimal
print(Decimal(num)) # 2.769999999999999574
It seems Decimal is only able to retain the precision if the number supplied is a float in the first place
print(Decimal(2.769999999999999574e+00)) # 2.769999999999999573674358543939888477325439453125
How do I maintain precision while converting a number from string to float?
Decimal(2.769999999999999574e+00) doesn't "retain precision". It only exposes the limitations of floating point arithmetic. The number you specify does not have an exact bit representation in memory, and is approximated to some degree, accounting for the extended precision you see here. This is a limitation of your (and any other) CPU. Decimal("2.769999999999999574e+00") on the other hand, is actually a more correct representation of your quantity than passing an actual float.

Are Python's decimal module calculations actually done with integers?

I'm using Python3's decimal module. Is the underlying arithmetic done using the processors floating point types, or does it use integers? The notion that the results are 'exact' and of arbitrary precision suggests to me that integer maths is used below the surface.
Indeed it is integer math, not float math for sure. Roughly speaking every float is two parts - before and after the decimal dot (integer and the remainder). Thanks to that the calculations are done using integer arithmetic and hence are not rounded up so they are staying precise even if you sum up a very large value with a very small fraction.
This comes at a price - the number of operations is significantly larger and it is not always necessary to be so precise at all times. That is why most of the calculations are done using float arithmetic that may cause a loss of precision when there are many arithmetic operations on floats or there are significant differences between the values (e.g. 10^10 ratio and more). There is a separate field of computer science: numerical analysis or numerical methods that study the clever ways to get the most of the speed of float calculations while maintaining highest precision possible.

directx local space coordinates float accuracy

I'm a bit confused of the local space coordinate system. Suppose I have a complex object in the local space. I know when I want to put it in the world space I have to multiply it with Scale,Rotate,Translate matrix. But the problem is the local coordinate only ranged from -1.0f to 1.0f, when I want to have vertex like (1/500,1/100,1/100) things will not work, everything will become 0 due to the float accuracy problem.
The only solution to me now is separate them into lots of local space systems and ProjectView each individually to put them together. It seems not the correct way of solving the problem. I've been checked lots of books but none of them mentioned this issue. I really want to know how to solve it.
when I want to have vertex like (1/500,1/100,1/100) things will not work
What makes you think that? The float accuracy problem does not mean something will coerce to 0 if it can't be accurately represented. It just means, it will coerce to the floating point number closest to the intended figure.
It's the very same as writing down, e.g., 3/9 with at most 6 significant decimal digits: 0.33334 – it didn't coerce to 0. And the very same goes for floating point.
Now you may be familiar with scientific notation: x·10^y – this is essentially decimal floating point, a mantissa x and an exponent y which essentially specifies the order of magnitude. In binary floating point it becomes x·2^y. In either case the significant digits are in the mantissa. Your typical floating point number (in OpenGL) has a mantissa of 23 bits, which boils down to an amount of 22 significant binary digits (which are about 7 decimal digits).
I really want to know how to solve it.
The real trouble with floating point numbers is, if you have to mix and merge numbers over a large range of orders of magnitudes. As long as the numbers are of similar order of magnitudes, everything happens with just the mantissa. And that one last change in order of magnitude to the [-1, 1] range will not hurt you; heck this can be done by "normalizing" the floating point value and then simply dropping the exponent.
Recommended read: http://floating-point-gui.de/
Update
One further thing: If you're writing 1/500 in a language like C, then you're performing an integer division and that will of course round down to 0. If you want this to be a floating point operation you either have to write floating point literals or cast to float, i.e.
1./500.
or
(float)1/(float)500
Note that casting one of the operands to float suffices to make this a floating point division.

lossless conversion of float to string and back: is it possible?

This question refers to the IEEE standard floating point numbers used on C/x86.
Is it possible to represent any numeric (i.e. excluding special values such as NaN) float or double as a decimal string such that converting that string back to a float/double will always yield exactly the original number?
If not, what algorithm tells me whether a given number will suffer a conversion error?
If so, consider this: some decimal fractions, when converted to binary, will not be numerically the same as the original decimal value, but the reverse is not true (because the binary has bounded precision so any decimal expansion is finite and perfect if not truncated), so here's another question...
Is it ever necessary to introduce deliberate errors into the decimal representation in order to trick the atof (or other) function into yielding the exact original number, or will a naive, non-truncating toString function be adequate (assuming exact conversion is possible in general)?
According to this page:
Actually, the IEEE754-1985 standard says that 17 decimal digits is
enough in all cases. However, it seems that the standard is a little
vague on whether conforming implementations must guarantee lossless
conversion when 17 digits are used.
So storing a double as a decimal string with at least 17 digits (correctly rounded) will guarantee that it can be converted back to binary double without any data loss.
In other words, if every single possible double-precision value were to be converted to a decimal string of 17 digits (correctly rounded), they will all map to different values. Thus there is no data-loss.
I'm not sure on the minimum cut-off for single-precision though. But I'd suspect that it will be 8 or 9 digits.
Given that the IEEE format can only represent a finite number of (binary) digits, and therefore have a minimum accuracy (cf. epsilon), you will only need a finite number of (decimal) digits. Of course it is preferable if the implementation (strtod, snprintf) has an identity mapping behavior between {all floats} and the set of {one decimal representation for each float}.
In java, it is possible to convert double from/to string, by constructing an intermediate BigDecimal object:
double doubleValue = ...;
// From double to string
String valueOfDoubleAsString = new BigDecimal(doubleValue).toString();
// And back
double doubleValueFromString = new BigDecimal(valueOfDoubleAsString).doubleValue();
// doubleValue == doubleValueFromString
There is no locale issue with this method.
However, special double values (Infinite, NaN) will of course not work.

Resources