Rails is messing up BigDecimals by throwing in Floating Point Logic - ruby-on-rails

I'm playing around with rails a bit and I have found something strange. For storing a money value I use the typical decimal data type which active record converts to BigDecimal. I considered this to be precise and I thought to avoid the odd behavior of floating point math. But when I store 99.99 to the db everything works fine, but when the records gets loaded by active record it loses precision and converts to something like 99.9899999999. This looks like a floating point issue.
I made some tests and found out that creating a BigDecimal like this b = BigDecimal.new("99.99") leads to a "clean" variable but building it this way b = BigDecimal.new(99.99) leads to the "unclean" version that I want to avoid.
I guess, that ActiveRecord reconstructs the BigDecimal with an intermediate float when loading the record from the database. This is not what I want and I would like to know if it can be avoided.
Ruby Version 1.9.3p0
Rails 3.2.9
Sqlite 3.7.9

Your problem is that you're using SQLite and SQLite doesn't have native support for numeric(m,n) data types. From the fine manual:
1.0 Storage Classes and Datatypes
Each value stored in an SQLite database (or manipulated by the database engine) has one of the following storage classes:
NULL. The value is a NULL value.
INTEGER. The value is a signed integer, stored in 1, 2, 3, 4, 6, or 8 bytes depending on the magnitude of the value.
REAL. The value is a floating point value, stored as an 8-byte IEEE floating point number.
TEXT. The value is a text string, stored using the database encoding (UTF-8, UTF-16BE or UTF-16LE).
BLOB. The value is a blob of data, stored exactly as it was input.
Read further down that page to see how SQLite's type system works.
Your 99.99 may be BigDecimal.new('99.99') in your Ruby code but it is almost certainly the REAL value 99.99 (i.e. an eight byte IEEE floating point value) inside SQLite and there goes the neighborhood.
So switch to a better database in your development environment; in particular, develop on top of whatever database you're going to be deploying on.

Don't use floating point for monetary values
Yes, exactly, SQLite is messing up your BigDecimal values.
The fundamental problem is that the FP format cannot store most decimal fractions correctly.
I believe you have about four choices:
Round everything to, say, two decimal places so that you never notice the slightly-off values.
Store your BigDecimal values in SQLite with the TEXT or BLOB storage class.
Use a different db that has some sort of decimal string support.
Scale everything to integral values and use the INTEGER storage class.
The problem is that FP fractions are rational numbers of the form x/2n. But the decimal monetary amounts have fractions that are x/(2n * 5m). The representations just aren't compatible. For example, in 0.01 ... 0.99, only 0.25, 0.50, and 0.75 have exact binary representations.

Related

Dart: best type to store currency

In Java currency is usually stored in BigDecimal. But what to use to store currency values in Dart? BigInt doesn't seem to be a solution because it is for integer values only.
Definitely do not use doubles for an accounting application. Binary floating point numbers inherently cannot represent exact decimal values, which can lead to inaccurate calculations for seemingly trivial operations. Even though the errors will be small, they can eventually accumulate into larger errors. Setting decimal precision on binary numbers doesn't really make sense.
For currency, you instead either should something intended to store decimal values (e.g. package:decimal) or should use fixed-point arithmetic to store cents (or whatever the smallest amount of currency is that you want to track). For example, instead of using doubles to store values such as $1.23, use ints to store amounts in the smallest unit of currency (e.g. 123 cents). Then you can use helper classes to format the amounts wherever they're displayed. For example:
class Money {
int cents;
Money({required this.cents});
#override
String toString() => (cents / 100).toStringAsFixed(2);
Money operator +(Money other) => Money(cents: cents + other.cents);
// Add other operations as desired.
}

Delphi - comparison of two "Real" number variables

I have problem with comparison of two variables of "Real" type. One is a result of mathematical operation, stored in a dataset, second one is a value of an edit field in a form, converted by StrToFloat and stored to "Real" variable. The problem is this:
As you can see, the program is trying to tell me, that 121,97 is not equal to 121,97... I have read
this topic, and I am not copletely sure, that it is the same problem. If it was, wouldn't be both the numbers stored in the variables as an exactly same closest representable number, which for 121.97 is 121.96999 99999 99998 86313 16227 83839 70260 62011 71875 ?
Now let's say that they are not stored as the same closest representable number. How do I find how exactly are they stored? When I look in the "CPU" debugging window, I am completely lost. I see the adresses, where those values should be, but nothing even similar to some binary, hexadecimal or whatever representation of the actual number... I admit, that advanced debugging is unknown universe to me...
Edit:
those two values really are slightly different.
OK, I don't need to understand everything. Although I am not dealing with money, there will be maximum 3 decimal places, so "currency" is the way out
BTW: The calculation is:
DATA[i].Meta.UnUsedAmount := DATA[i].AMOUNT - ObjQuery.FieldByName('USED').AsFloat;
In this case it is 3695 - 3573.03
For reasons unknown, you cannot view a float value (single/double or real48) as hexadecimal in the watch list.
However, you can still view the hexadecimal representation by viewing it as a memory dump.
Here's how:
Add the variable to the watch list.
Right click on the watch -> Edit Watch...
View it as memory dump
Now you can compare the two values in the debugger.
Never use floats for monetary amounts
You do know of course that you should not use floats to count money.
You'll get into all sorts of trouble with rounding and comparisons will not work the way you want them too.
If you want to work with money use the currency type instead. It does not have these problems, supports 4 decimal places and can be compared using the = operator with no rounding issues.
In your database you use the money or currency datatype.

Is double ok for geo-coordinates in Java?

Hi i want to work with geocoordinates in java.
I've defined my Java variables as "double" and my Postgres Database is defined as "double precision".
I've heared of problems with float that 0.1 results sometimes in 0.09999...
It will have to store values like 50.081406 or 8.24481.
The values will be read from an Android-Device.
Do i have to worry about floating-point problems?
The issue with the floats is usually related to addition and subtraction where due to 2's compliment (how they are stored on the computer) they don't always round out exactly to what you want. That being said Doubles are a great way to store lat/lon
Also see: proper/best type for storing latitude and longitude

Float Value being Truncated in RoR 3.2

When saving my longtitude and latitude in my Rails 3.2 app, the value's getting truncated on save.
I've tried in the console and it's saving the full value:
item.update_attributes(:latitude => '51.07763839854951')
item.latitude:
=> 51.07763839854951
Saving the same value in the browser gives me an output of:
51.0865174
What can I do to prevent this?
The value is probably being truncated when it is stored in the DB. Regardless of the exact cause, if this value is precise and needs to be stored and retrieved losslessly, a floating-point number is probably the wrong data type to use. Floating point numbers can lose precision when you perform certain arithmetic operations on them, so they are not appropriate for values which must be exact.
When you are defining your DB schema using Rails migrations, you can use the :decimal type for decimal values which must be stored precisely. (When ActiveRecord pulls these values out of the DB, they will become BigDecimal objects rather than Floats You can do arithmetic on BigDecimals without losing precision.)

In Delphi how do I determine when to use Real, Real48, Double or Single data types?

Most of my applications revolve around financial calculations involving payments and interest rate calculations. I'm looking to find out how to determine what Delphi data type is best to use.
If I'm using a database to store these values and I've defined the fields in that database to be a decimal value with two decimal places, which Delphi datatype is most compatible with that scenario?
Should I use a rounding formula in Delphi to format the results to two decimal places before storing the values in the database? If so what is a best practice for doing so?
For such calculations, don't use floating point types like Real, Single or Double. They are not good with decimal values like 0.01 or 1234.995, as they must approximate them.
You can use Currency, a fixed point type, but that is still limited to 4 decimal places.
Try my Decimal type, which has 28-29 places and has a decimal exponent so it is ideal for such calculations. The only disadvantage is that it is not FPU supported (but written in assembler, nevertheless) so it is not as fast as the built-in types. It is the same as the Decimal type used in .NET (but a little faster) and quite similar to the one used on the Mac.
If you want to do financial calculations, don't use any of the floating-point/real types. Delphi has a Currency type, which is a fixed-point value with 4 decimal places, that should be just what you need.

Resources