0

I am trying to convert an arbitrary string (whereby the decimal point is explicitly manipulated controlled) to a number (i.e. floating point) using string-to-number and I am running into either a rounding issue or some kind of precision issue when Emacs performs the conversion. I am not a floating point expert so my exact terminology may be slightly incorrect, please pardon me.

The problem is interment based on the value (leading me to believe it's one of the aforementioned issues) provided, however I have an example reproducible case:

Example code:

(string-to-number "8807240867.266221")
;; Expected: 8807240867.266221
;; Actual:   8807240867.26622

Standard environment disclosure:

  • OS: MacOS
  • Chip: Intel
  • Emacs Version: 27.2.1
  • Port: Mitsuharu Yamamoto / Railwaycat
  • Other Notes: - No special distributions / etc.
  • Relevant Common Packages Used:
    • Standard internal Emacs packages

The trailing 1 is removed when I would not expect it to be. I have searched the C-code for how string-to-number works, however I am quite limited in my C knowledge so my best guess is that somehow the value is overflowing somewhere down the line? However, I don't receive an error message of any kind so that is what makes me think it's some kind of rounding/precision problem instead.

Any help is appreciated in explaining why this kind of issue is occurring, what exact issue is occurring, how to prevent it/properly fix it so that no digits are unexpectedly chopped off.

8
  • "On modern platforms, floating-point operations follow the IEEE-754 standard closely; however, results are not always rounded correctly on some obsolescent platforms, notably 32-bit x86."
    – shynur
    Jun 15, 2023 at 2:53
  • @shynur I haven't dug into the IEEE-754 standard but macOS has been a 64 bit OS for years now and AFAIK it's been even longer since Apple sold 32-bit Macs. I assume that is what you're getting at but I'm not sure. Could you elaborate more?
    – David
    Jun 15, 2023 at 3:02
  • Similar questions here and here - also the references in the latter.
    – NickD
    Jun 15, 2023 at 3:03
  • 1
    No, it has nothing to do with incorrect rounding on some old platforms. What you are seeing is an inherent problem in floating-point numbers: they use a finite number of bits, so they are inherently discrete - they cannot represent continuous values (i.e real numbers) exactly. In the most common representation on 64-bit platforms, a floating-point number is represented in 64 bits: 53 bits of significand and 11 bits of exponent. That's equivalent to a precision of just under 16 decimal digits (so your number is just out of that range). Try (= 8807240867.266221 8807240867.26622).
    – NickD
    Jun 15, 2023 at 3:21
  • 1
    Yes, I think that is right. You could use quad-precision (or higher - see IEEE754 for some details of various representations).
    – NickD
    Jun 15, 2023 at 3:44

1 Answer 1

3

What Every Programmer Should Know About Floating-Point Arithmetic is an excellent guide to understanding floating point numbers. I thoroughly recommend reading that in full -- I think it will answer your question in detail, and more besides.

The essence, though, is that floating point numbers are inherently imprecise, so these kinds of issues are expected. If you need greater precision (which is a necessity for many applications) then floating point is simply the wrong format to be using.

Note that the rounding has nothing to do with string-to-number. All you're seeing is the nearest floating-point representation for that value. You can observe the same result simply by evaluating the number itself (i.e. getting the lisp reader to generate a float object from the code text):

(string-to-number "8807240867.266221")
=> 8807240867.26622

8807240867.266221
=> 8807240867.26622

See C-hig (elisp)Float Basics for an elisp-specific discussion.

See also https://0.30000000000000004.com/ for a well-known example calculation repeated in many programming languages.

3
  • I read through that site. So, right, it is a precision issue due to Emacs internally being forced to round. However, I am still at a loss on how to solve this problem (i.e. maintaining precision) then. ELisp doesn't have data types for numbers beyond integers and decimals. BigNum was added in 27.x but as an internal implementation detail not a fully supported type. Clearly, ELisp's number type can't support this. What then is the best way to represent decimals in ELisp? Strings?
    – David
    Jun 15, 2023 at 3:11
  • It has nothing to do with Emacs: Emacs is just using what the hardware provides. It would be the same in any language using floating-point numbers (on the same hardware). The solution is to use an arbitrary-precision arithmetic library. Emacs Calc provides extended precision: you just have to specify what precision you want and you will get it (at the cost of memory and time).
    – NickD
    Jun 15, 2023 at 3:40
  • It depends on what you're doing, really. I'm inclined to also recommend calc as the in-built and well-tested solution. The bignum support is notable in that decimal fractions are just integers divided by some power of 10, so you can use integers as an internal representation for fractions without too much difficulty (again, depending on what you're doing), and bignums mean you won't lose precision; but I'm not sure if there's any pre-written library for that approach.
    – phils
    Jun 15, 2023 at 4:00

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.