Is floating point math more precise for values close to unity?

Viewed 430

Question

I've been told many times that floating point arithmetic has the greatest precision if the numbers operated on are close to 1.0 (or sometimes 0.1). Is there any truth to this?

Clearifications

With "arithmetic" I mean things like a + b, a * b, a / b, but also sqrt(x) and other mathy functions.

To be specific, let's say that all variables are IEEE 64 bit double precision floating point numbers.

Example

In physics simulation code, physical units are usually incorporated by mapping them to floating point values. Here we have a lot of freedom, but one choice is to go with the SI/metric system, something like

# Base units
m  = 1.0  # metre
s  = 1.0  # second
kg = 1.0  # kilogram
# Derived units
km = 1e+3*m             # kilometre
yr = 60*60*24*365.25*s  # year
m_sun = 1.98841e+30*kg  # mass of the sun
c = 299792458*m/s       # speed of light
...

The numerical value of any dimensional variable in such a code depends on the choice of unit system. If we get out a value x == 1.2e-9 and x is meant to be understood as a e.g. a length, we know that this means that x is 1.2 nanometres. Had we chosen to set m = 1e-9, x would instead come out with a value of 1.2, as we are now working in a unit system where the nanometre is the base length unit.

Depending on the physical system studied within the simulation, different "natural" unit systems might be chosen. If our focus is atomic physics, it's probably not ideal to choose the mass of the sun as the base mass unit. Well why not? That's my question. Sure all masses of interest would then have tiny numerical values, but so what? Would the inherent imprecisions of floating point arithmetic somehow be magnified by working with extremely small/large numbers?

I'm aware that a minimum and maximum floating point number exists (something like 1e-324 and 1e+308). Using a unit system so out of wack for the task at hand that the values of our variables go beyond these limits is of course destructive. Keeping values well within those bars though, is there really any difference to whether typical values in the code are of order 1.0, 1e±10, 1e±100?

Note about math functions

Various math functions are in fact noticeable imprecise given very large/small inputs. As an example, cos(1e-8) == 1, i.e. the cos() function cannot distinguish positive numbers smaller than 1e-8. This is not relevant to my question, as the input to cos() must always be a dimensionless, pure number, i.e. independent of the unit system defined in the code. The same goes for all other trigonometric functions, but also exp(), log() and others.

1 Answers

Is floating point math more precise for values close to unity?

Not really.

In general, floating point math well preserves realtive precision for *, /, sqrt() cover the lion’s share of the floating point range. +, - are subject to significant loss of relative precision (to the result) due to subtraction of nearby values.

Overall, there is little difference for normal numbers for relative precision. It varies from (0.5 to 1.0] * 2-53.

The absolute precision changes in steps for powers of 2.

Floating point numbers [0.5...1.0) have the same absolute precision. For double 2-54.
Floating point numbers [1.0...2.0) have the same absolute precision. For double 2-53.
Floating point numbers [2.0...4.0) have the same absolute precision. For double 2-52.
Floating point numbers [4.0...8.0) have the same absolute precision. For double 2-51.
etc.

floating point arithmetic has the greatest precision if the numbers operated on are close to 1.0 (or sometimes 0.1). Is there any truth to this?

Values just under a power-of-2 have a higher absolute precision (by about 2x) than values just above a power of 2.

With tiny subnormal values, precision is lost, a bit per power of 2 until 0.0 is reached.


Advanced: Trig functions has a special concern when their magnitude is large. A high quality sin(1e10) does an internal extended high precision argument reduction to the primary [-pi ... pi] range. Not all trig function implementations handle this step well. So for radians arguments, starting in the primary range is useful to maintain precision. For degree arguments, a simple fmod(deg, 360.0) is a simple and precise range reduction.

Related