in reply to floating point addition

This is also the reason why processors have a “decimal mode,” in which both digits and the sign of the number are represented using four-bit groups (so-called “binary-coded decimal = BCD.”).   The COBOL language is famous for exposing this feature, and for very obvious reasons:   when dollars-and-cents are involved, accountants (especially) care very much about “cents.”   The arithmetic will be performed in true decimal mode, just like they taught you to do it by-hand in grade school.   (Uhhh-h-h-h, back in my day, anyhow, when to use a pocket calculator was considered “cheating.”)

Perl does have a Math::Decimal package, which probably exposes the functionality that you might need here.   (It says that it is “implemented mostly in XS,” but I have not taken the time to see whether it uses microprocessor decimal-math.)   A package like this one will not be able to represent 1/3 exactly, simply because “base-10 can’t do that,” but it will be able to represent 1/10 exactly.

Incidentally, if this is a design consideration, you must also use DECIMAL types in your databases, and be certain that the entire soup-to-nuts handling of the numeric values does not ever stray into float-binary.   (You must also confirm that the underlying representation used by the database system in question is, in fact, decimal.)   Databases may also offer a CURRENCY type that, in most cases, is actually a scaled binary integer.   (Internally, the number is multiplied by 10,000 to give four fixed digits to the right of the decimal point.)   Once again, you must ensure that the entire data production-line never wanders into float-binary -land.

Perl does have the capability to represent a value internally as a true integer, although the bit-width of that integer may vary by implementation.   Since it is a typeless language that tries its best to be accommodating, you must be mindful of exactly how Perl is representing and manipulating your values at all times ... if it matters.   (As it certainly does, say when you are dealing with the final, double-underlined total at the bottom of a big, long invoice ...)

Replies are listed 'Best First'.
Re^2: floating point addition
by syphilis (Archbishop) on Jan 12, 2015 at 23:35 UTC
    It says that it is “implemented mostly in XS,” but I have not taken the time to see whether it uses microprocessor decimal-math

    I don't think it does, but it certainly does decimal (as opposed to binary) arithmetic - and should be more than adequate for most people wanting to perform base 10 arithmetic.

    OTOH, my Math::Decimal64 and Math::Decimal128 modules do make use of the _Decimal64 and _Decimal128 types, and associated operations:
    C:\>perl -MMath::Decimal64 -le "$x=Math::Decimal64->new(0); $x += Math +::Decimal64->new('20.1') for (1..43);print $x;" 8643e-1
    The user interface is a little awkward, mainly because gcc does not provide strtod64 or strtod128 functions, and does not provide any (s)printf formatters for the _Decimal64 and _Decimal128 type.

    I'm working on improving that interface - and the current git version provides for output in floating point format instead of just scientific notation (ie as 864.3 instead of 8643e-1 for the quoted example).
    I must also add overloading of strings - so that the above one liner can be rewritten as:
    perl -MMath::Decimal64 -le "$x=Math::Decimal64->new(0); $x += '20.1' f +or (1..43);print $x;"
    But I don't want to add overloading of NVs as that would defeat the purpose.

    (That takes care of this month's quota of self-promotion ;-)

    Cheers,
    Rob
      But I don't want to add overloading of NVs as that would defeat the purpose.

      How would this defeat the purpose?

        Because allowing use of NV values re-introduces the very rounding anomalies we're (normally) trying to avoid:
        C:\>perl -MMath::Decimal64=":all" -le "$x=Math::Decimal64->new('0.085' +); $y = Math::Decimal64->new(0.085);$z = NVtoD64(0.085);print $x;prin +t $y; print $z;" Outputs: 85e-3 8500000000000001e-17 8500000000000001e-17
        $y and $z are assigned from the actual NV value, whereas $x is assigned the specified decimal value.

        The trouble with allowing overloading of NVs is that it makes it just too easy for people to inadvertently use a value that they didn't really mean to use - though I guess the same criticism could be levelled at new() because *it* currently allows assignment of NVs.
        NVtoD64() ought to be there so that one *can* assign the NV to a Math::Decimal64 object if one wants. It's name pretty much explicitly indicates what it's going to do, thus making it less likely that one will inadvertently assign an unintended value.

        Cheers,
        Rob