www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - std.math.round() - doc bug?

reply Stefan Zobel <Stefan_member pathlink.com> writes:
DMD 0.133 (Win XP)

The documentation for "real round(real x)" says:

# Return the value of x rounded to the nearest integer.
# If the fractional part of x is exactly 0.5,
# the return value is rounded to the even integer.

In reality, it seems to round the "halfway cases" (fractional part = 0.5)
away from zero. Considering that round() delegates to std.c.math.roundl()
that's not surprising (AFAIK, roundl() is defined to round "away from zero").

It appears that I have to use "real nearbyint(real x)" to achieve the
desired "round to the even integer" behavior?

I'm by no means an expert in this matter. Can anyone confirm that this
is a doc bug?

Best regards,
Stefan
Sep 28 2005
parent reply Don Clugston <dac nospam.com.au> writes:
Stefan Zobel wrote:
 DMD 0.133 (Win XP)
 
 The documentation for "real round(real x)" says:
 
 # Return the value of x rounded to the nearest integer.
 # If the fractional part of x is exactly 0.5,
 # the return value is rounded to the even integer.
 
 In reality, it seems to round the "halfway cases" (fractional part = 0.5)
 away from zero. Considering that round() delegates to std.c.math.roundl()
 that's not surprising (AFAIK, roundl() is defined to round "away from zero").
 
 It appears that I have to use "real nearbyint(real x)" to achieve the
 desired "round to the even integer" behavior?
 
 I'm by no means an expert in this matter. Can anyone confirm that this
 is a doc bug?
 
 Best regards,
 Stefan

It's definitely a doc bug. Here's the behaviour of the existing functions: round() --- rounds away from zero trunc() --- rounds towards zero floor() --- rounds towards negative infinity ceil() --- rounds towards positive infinity nearbyint() --- round to even (use current rounding mode) rint() --- round to even (use current rounding mode) This exactly matches the C99 behaviour. But it seems that there is no way to ensure round-to-even if the current rounding mode is changed. I think that's a deficiency in C that D should rectify. -Don
Sep 29 2005
parent reply Stefan Zobel <Stefan_member pathlink.com> writes:
In article <dhg7cp$1m8d$1 digitaldaemon.com>, Don Clugston says...
Stefan Zobel wrote:
 DMD 0.133 (Win XP)
 
 The documentation for "real round(real x)" says:
 
 # Return the value of x rounded to the nearest integer.
 # If the fractional part of x is exactly 0.5,
 # the return value is rounded to the even integer.
 
 In reality, it seems to round the "halfway cases" (fractional part = 0.5)
 away from zero. Considering that round() delegates to std.c.math.roundl()
 that's not surprising (AFAIK, roundl() is defined to round "away from zero").
 
 It appears that I have to use "real nearbyint(real x)" to achieve the
 desired "round to the even integer" behavior?
 
 I'm by no means an expert in this matter. Can anyone confirm that this
 is a doc bug?
 
 Best regards,
 Stefan

It's definitely a doc bug. Here's the behaviour of the existing functions: round() --- rounds away from zero trunc() --- rounds towards zero floor() --- rounds towards negative infinity ceil() --- rounds towards positive infinity nearbyint() --- round to even (use current rounding mode) rint() --- round to even (use current rounding mode) This exactly matches the C99 behaviour. But it seems that there is no way to ensure round-to-even if the current rounding mode is changed. I think that's a deficiency in C that D should rectify. -Don

Thanks for the clarification Don. BTW, how can the current rounding mode be changed? Other rounding modes are "towards negative infinity" and "towards positive infinity" (converting rint() to floor() / ceil()), right? Thanks, Stefan
Sep 29 2005
parent reply Don Clugston <dac nospam.com.au> writes:
Stefan Zobel wrote:
 In article <dhg7cp$1m8d$1 digitaldaemon.com>, Don Clugston says...
 
Stefan Zobel wrote:

DMD 0.133 (Win XP)

The documentation for "real round(real x)" says:

# Return the value of x rounded to the nearest integer.
# If the fractional part of x is exactly 0.5,
# the return value is rounded to the even integer.

In reality, it seems to round the "halfway cases" (fractional part = 0.5)
away from zero. Considering that round() delegates to std.c.math.roundl()
that's not surprising (AFAIK, roundl() is defined to round "away from zero").

It appears that I have to use "real nearbyint(real x)" to achieve the
desired "round to the even integer" behavior?

I'm by no means an expert in this matter. Can anyone confirm that this
is a doc bug?

Best regards,
Stefan

It's definitely a doc bug. Here's the behaviour of the existing functions: round() --- rounds away from zero trunc() --- rounds towards zero floor() --- rounds towards negative infinity ceil() --- rounds towards positive infinity nearbyint() --- round to even (use current rounding mode) rint() --- round to even (use current rounding mode) This exactly matches the C99 behaviour. But it seems that there is no way to ensure round-to-even if the current rounding mode is changed. I think that's a deficiency in C that D should rectify. -Don

Thanks for the clarification Don. BTW, how can the current rounding mode be changed? Other rounding modes are "towards negative infinity" and "towards positive infinity" (converting rint() to floor() / ceil()), right? Thanks, Stefan

There are four rounding modes in IEEE hardware -- nearest, floor, ceil, and trunc(). The round() function in C99 ("high school rounding") is not supported by the hardware, so it's significantly slower. I think it's ridiculous that the most accurate mode is not supported by C99. I also note that in D, cast(int)(x) uses trunc(), following the (stupid and slow) C behaviour. float.html describes how to change the rounding mode. It currently says "[blah, blah, blah]". In a post in dm.D yesterday, I asked Walter what his plans are. He doesn't yet have any. In C, depending on the compiler, it's often done in a user-hostile way with _control87(). Less readable and less portable than asm. C99 has fegetround() and fesetround() in fenv.h. A more D-like alternative might be with properties; something like: if (real.rounding != roundfloor) { typeof(real.rounding) oldmode = real.rounding; real.rounding = roundeven; real.rounding = oldmode; } but at present, all properties for built-in types are read-only. I don't know if writable types would be a problem. Rationale: the rounding mode behaves as if it is a static variable which applies to every floating-point type. Ditto for precision. Downside: it applies to every floating point type, including cfloat and idouble, which in D are not derived from real. I haven't worked out how exceptions could work with properties, though. Do you have any opinions/ideas on any of this? -Don.
Sep 29 2005
parent Stefan Zobel <Stefan_member pathlink.com> writes:
In article <dhgqgm$2ajg$1 digitaldaemon.com>, Don Clugston says...
Stefan Zobel wrote:
 In article <dhg7cp$1m8d$1 digitaldaemon.com>, Don Clugston says...
It's definitely a doc bug.
Here's the behaviour of the existing functions:

round() --- rounds away from zero
trunc() --- rounds towards zero
floor() --- rounds towards negative infinity
ceil()  --- rounds towards positive infinity

nearbyint() --- round to even (use current rounding mode)
rint()      --- round to even (use current rounding mode)

This exactly matches the C99 behaviour. But it seems that there
is no way to ensure round-to-even if the current rounding mode
is changed.  I think that's a deficiency in C that D should rectify.

-Don

Thanks for the clarification Don. BTW, how can the current rounding mode be changed? Other rounding modes are "towards negative infinity" and "towards positive infinity" (converting rint() to floor() / ceil()), right? Thanks, Stefan

There are four rounding modes in IEEE hardware -- nearest, floor, ceil, and trunc(). The round() function in C99 ("high school rounding") is not supported by the hardware, so it's significantly slower. I think it's ridiculous that the most accurate mode is not supported by C99.

Don, thank you for the exemplification. However, I don't understand what your "most accurate" mode is (nearest?). And slightly OT: Why wouldn't it be supported by C99? I'd assume that nearbyint/rint take advantage of CPU instructions, or are they missing from the C99 standard?
I also note that in D, cast(int)(x) uses trunc(), following the (stupid 
and slow) C behaviour.

float.html describes how to change the rounding mode. It currently says 
"[blah, blah, blah]". In a post in dm.D yesterday, I asked Walter what 
his plans are. He doesn't yet have any.

Ah, I see.
In C, depending on the compiler, it's often done in a user-hostile way 
with _control87(). Less readable and less portable than asm.
C99 has fegetround() and fesetround() in fenv.h.

A more D-like alternative might be with properties; something like:

if (real.rounding != roundfloor) {
    typeof(real.rounding) oldmode = real.rounding;
    real.rounding = roundeven;

    real.rounding = oldmode;
}

but at present, all properties for built-in types are read-only.
I don't know if writable types would be a problem.
Rationale: the rounding mode behaves as if it is a static variable
which applies to every floating-point type. Ditto for precision.
Downside: it applies to every floating point type, including cfloat and 
idouble, which in D are not derived from real.

Personally, I'd prefer a library call for that very reason, but that's just me. Best regards, Stefan
I haven't worked out how exceptions could work with properties, though.

Do you have any opinions/ideas on any of this?

-Don.

Sep 29 2005