digitalmars.D - Floating point rounding modes: we should restrict them slightly
- Don (31/31) Sep 10 2009 A quiz:
- #ponce (7/7) Sep 10 2009 Why not imposing a rounding mode (ex : toward minus infinity) ?
- Don (10/12) Sep 10 2009 You can't impose a rounding mode in a system language: rounding mode is
- Jimbob (5/13) Sep 10 2009 There's more to rounding modes than just converting / rounding to intege...
- Lionello Lunesu (3/13) Sep 10 2009 Shouldn't ceil(1.0) return 1.0, and wouldn't floor(1.0 + 1.0L) return 2....
- Rainer Deyke (4/7) Sep 10 2009 Even if that function is called 'fesetround'?
- Don (4/10) Sep 10 2009 Obviously not. In fact, I'd hope to even disallow fesetround() since
- Michel Fortin (22/33) Sep 10 2009 I perfectly agree. I'm just wondering how to enforce it in the language.
- Don (12/50) Sep 10 2009 Yes, it's not so obvious how to enforce it. However, in reality it's a
-
Stewart Gordon
(14/28)
Sep 11 2009
- Don (9/38) Sep 13 2009 I already presented a proposal for that. This proposal is primarily for
- Stewart Gordon (10/19) Sep 13 2009 Actually, you _can_ avoid changing the floating point settings in the
- Don (23/46) Sep 14 2009 The hardware dependency is still there, in every function which uses
- Rainer Deyke (13/19) Sep 14 2009 Don't forget that "uses floating point" is a transitive property. Any
- Don (20/38) Sep 14 2009 This is incorrect. You've got it backwards. It's not "uses floating
- Rainer Deyke (18/34) Sep 14 2009 Then I must have misunderstood your proposal. Either you did a poor job
- Don (16/50) Sep 15 2009 The description I just gave of it was
- Rainer Deyke (20/29) Sep 15 2009 No, I saw that. But if a (pure) function calls another (pure) function
- Don (19/48) Sep 16 2009 Ah. The important thing that makes this work is that the global FP state...
- Rainer Deyke (13/18) Sep 16 2009 Then I'm not sure it makes to treat "memoisable" as a property of a
- Don (13/30) Sep 17 2009 It sounds as though you never saw my proposal. My proposal was that, by
- Jason House (2/7) Sep 14 2009 FWIW, I've liked your proposals, but I don't feel qualified to comment. ...
- Denis Koroskin (3/14) Sep 14 2009 I second that. Nothing to add.
- Walter Bright (4/10) Sep 11 2009 But that doesn't allow for changing the rounding mode at a global level,...
- Don (13/24) Sep 13 2009 It doesn't prevent that at all. You just need to change the rounding
- Michel Fortin (17/23) Sep 13 2009 Problems still may occur if x and y do memoization however. You call
- Don (21/35) Sep 13 2009 It's not a big deal, because rounding mode is changed so rarely, so you
- Walter Bright (2/13) Sep 13 2009 Ok, I understand now. This makes sense.
- Walter Bright (2/8) Sep 13 2009 I added it into float.html for D2.
- bearophile (5/14) Sep 13 2009 An important purpose of a not bug-prone language is remove as many undef...
- Walter Bright (5/13) Sep 13 2009 Unlikely. But that has to be weighed against the former behavior being
- Don (12/29) Sep 14 2009 We could get close, I think. We could catch all violations in SafeD.
- Walter Bright (4/28) Sep 14 2009 Yes, all the C functions will have to be gone through and separated.
- bearophile (5/7) Sep 14 2009 For some of the removed functions better C-like ones can be provided:
- Don (2/11) Sep 14 2009 Just use D instead.
- Brad Roberts (3/13) Sep 14 2009 You sure? Does running beyond the bounds of the array if there's no nul...
- Walter Bright (5/10) Sep 14 2009 Yes. Memory safety is defined as being free of memory corruption errors....
- Don (6/18) Sep 15 2009 It does result in undefined behaviour, though. I don't see much
A quiz: Conside a function real foo() {....} This is not pure, so that it may side-effects, but even so, are listings one and two equivalent? LISTING ONE: real x = foo(1) + foo(2) + foo(3); LISTING TWO: real x1 = foo(1); real x2 = foo(2); real x3 = foo(3); real x = x1 + x2 + x3; In C and C++ (and currently in D), they are NOT equivalent! foo() is allowed to change the floating-point rounding mode, so it could change the meaning of '+'. This is quite ridiculous. This is a 'freedom' that comes with a heavy price tag. Actually doing this would make code nearly impossible to debug, and I don't think anyone is stupid enough to actually do this ...but as long as it's theoretically possible, it prevents the compiler from doing useful optimisations. Disallowing this kind of obscure, dangerous nonsense is, I think, part of the core mission of D. PROPOSAL: Change the spec by adding the line to float.html: "If the floating-point rounding mode is changed within a function, it must be restored before the function exits. If this rule is violated (for example, by the use of inline asm), the rounding mode used for subsequent calculations is undefined." This slight tightening of semantics can have a significant benefit for all floating-point code.
Sep 10 2009
Why not imposing a rounding mode (ex : toward minus infinity) ? And then disallowing to change it in assembly. I mean, you can still do : round(x) as floor(x + 0.5L) ceil(x) as floor(x + 1.0L) Iit would cost some precision with large floating-point number, but not much since rounding such large numbers has few meaning. It would clear up the rounding-mode mess a bit.
Sep 10 2009
#ponce wrote:Why not imposing a rounding mode (ex : toward minus infinity) ? And then disallowing to change it in assembly.You can't impose a rounding mode in a system language: rounding mode is a crucial feature in a few rare but very important cases. The trick is to allow those cases without polluting everything else. This tiny proposal gets rid of most the damage. However, my previous proposal for fixing the interaction of 'pure' with rounding modes and exception handling, would allow the rounding mode to be round-to-nearest almost everywhere, and changeable only in impure functions inside modules marked module(advancedfloatingpoint). This could give performance benefits everywhere outside those modules.
Sep 10 2009
"#ponce" <aliloko gmail.com> wrote in message news:h8adr2$23p3$1 digitalmars.com...Why not imposing a rounding mode (ex : toward minus infinity) ? And then disallowing to change it in assembly. I mean, you can still do : round(x) as floor(x + 0.5L) ceil(x) as floor(x + 1.0L) Iit would cost some precision with large floating-point number, but not much since rounding such large numbers has few meaning. It would clear up the rounding-mode mess a bit.There's more to rounding modes than just converting / rounding to integer. Rounding affects all arithmetic operations, and whether an expresion evaluates to 0.97999 or .97998 can be important in some cases.
Sep 10 2009
#ponce wrote:Why not imposing a rounding mode (ex : toward minus infinity) ? And then disallowing to change it in assembly. I mean, you can still do : round(x) as floor(x + 0.5L) ceil(x) as floor(x + 1.0L) Iit would cost some precision with large floating-point number, but not much since rounding such large numbers has few meaning. It would clear up the rounding-mode mess a bit.Shouldn't ceil(1.0) return 1.0, and wouldn't floor(1.0 + 1.0L) return 2.0? L.
Sep 10 2009
Don wrote:Change the spec by adding the line to float.html: "If the floating-point rounding mode is changed within a function, it must be restored before the function exits.Even if that function is called 'fesetround'? -- Rainer Deyke - rainerd eldwood.com
Sep 10 2009
Rainer Deyke wrote:Don wrote:Obviously not. In fact, I'd hope to even disallow fesetround() since it's too easy to abuse, and instead have an RAII struct to restore the mode automatically. But that's just syntax sugar.Change the spec by adding the line to float.html: "If the floating-point rounding mode is changed within a function, it must be restored before the function exits.Even if that function is called 'fesetround'?
Sep 10 2009
On 2009-09-10 04:24:38 -0400, Don <nospam nospam.com> said:PROPOSAL: Change the spec by adding the line to float.html: "If the floating-point rounding mode is changed within a function, it must be restored before the function exits. If this rule is violated (for example, by the use of inline asm), the rounding mode used for subsequent calculations is undefined." This slight tightening of semantics can have a significant benefit for all floating-point code.I perfectly agree. I'm just wondering how to enforce it in the language. Later in the thread you say:... have an RAII struct to restore the mode automatically. But that's just syntax sugar.But how do you prevent someone from writing: auto s = new roundingMode(...); The only way you can really make sure it's scoped to a specific function is to call it from another function: R performWithRoundingMode(R)(flag roundingMode, lazy R result) { flag oldRoundingMode = getRoundingMode(); setRoundingMode(roundingMode); try return result; finally setRoundingMode(oldRoundingMode); } auto i = performWithRoundingMode(TOWARDS_ZERO, 12 * 12); Here you can't escape the scoping requirements. -- Michel Fortin michel.fortin michelf.com http://michelf.com/
Sep 10 2009
Michel Fortin wrote:On 2009-09-10 04:24:38 -0400, Don <nospam nospam.com> said:Yes, it's not so obvious how to enforce it. However, in reality it's a rarely-used, system level feature, so relying on convention probably isn't too terrible in the short term.PROPOSAL: Change the spec by adding the line to float.html: "If the floating-point rounding mode is changed within a function, it must be restored before the function exits. If this rule is violated (for example, by the use of inline asm), the rounding mode used for subsequent calculations is undefined." This slight tightening of semantics can have a significant benefit for all floating-point code.I perfectly agree. I'm just wondering how to enforce it in the language.Later in the thread you say:scope _should_ take care of that. I'm not sure that it does. Still, we just need to make it easy to do the right thing, and difficult to do the wrong thing.... have an RAII struct to restore the mode automatically. But that's just syntax sugar.But how do you prevent someone from writing: auto s = new roundingMode(...);The only way you can really make sure it's scoped to a specific function is to call it from another function: R performWithRoundingMode(R)(flag roundingMode, lazy R result) { flag oldRoundingMode = getRoundingMode(); setRoundingMode(roundingMode); try return result; finally setRoundingMode(oldRoundingMode); } auto i = performWithRoundingMode(TOWARDS_ZERO, 12 * 12); Here you can't escape the scoping requirements.Hmmm. Not ideal, but could be worse. But anyway, the important thing is to codify in the spec that the floating point state must not change randomly. For discussing syntax, I think we really need an interval arithmetic module, so that we can ensure it all works well.
Sep 10 2009
Don wrote: <snip>LISTING ONE: real x = foo(1) + foo(2) + foo(3); LISTING TWO: real x1 = foo(1); real x2 = foo(2); real x3 = foo(3); real x = x1 + x2 + x3; In C and C++ (and currently in D), they are NOT equivalent! foo() is allowed to change the floating-point rounding mode, so it could change the meaning of '+'.<snip> This is just one aspect of it. You could also call a pure function, change the rounding mode, and then call the pure function again on the same arguments. The value returned would change, thereby violating the purity. There has been some talk about pure functions in relation to locales: http://d.puremagic.com/issues/show_bug.cgi?id=3057 http://www.digitalmars.com/d/archives/digitalmars/D/std.locale_85081.html Floating point settings are just another case of the same thing, except that currently violations in relation to the former are allowed. Maybe the right solution is some kind of hidden parameter mechanism.... Stewart.
Sep 11 2009
Stewart Gordon wrote:Don wrote: <snip>I already presented a proposal for that. This proposal is primarily for functions which are not pure.LISTING ONE: real x = foo(1) + foo(2) + foo(3); LISTING TWO: real x1 = foo(1); real x2 = foo(2); real x3 = foo(3); real x = x1 + x2 + x3; In C and C++ (and currently in D), they are NOT equivalent! foo() is allowed to change the floating-point rounding mode, so it could change the meaning of '+'.<snip> This is just one aspect of it. You could also call a pure function, change the rounding mode, and then call the pure function again on the same arguments. The value returned would change, thereby violating the purity.There has been some talk about pure functions in relation to locales: http://d.puremagic.com/issues/show_bug.cgi?id=3057 http://www.digitalmars.com/d/archives/digitalmars/D/std.locale_85081.html Floating point settings are just another case of the same thing, except that currently violations in relation to the former are allowed.There's a fundamental difference between them: the floating point settings are a hardware feature and it is IMPOSSIBLE to avoid them. You can choose not to use locale settings. Or, you can pass them as a parameter, which doesn't work for floating point settings. Please do not get sidetracked on pure functions, it is orthogonal to this issue.
Sep 13 2009
Don wrote: <snip>Actually, you _can_ avoid changing the floating point settings in the course of an average program. But still....Floating point settings are just another case of the same thing, except that currently violations in relation to the former are allowed.There's a fundamental difference between them: the floating point settings are a hardware feature and it is IMPOSSIBLE to avoid them.You can choose not to use locale settings. Or, you can pass them as a parameter, which doesn't work for floating point settings.As long as it's the built-in arithmetic operators you're concerned about. But that said, wrapping every arithmetic operation in a function to purify it of dependence on floating point settings wouldn't be ideal.Please do not get sidetracked on pure functions, it is orthogonal to this issue.I'm not sure about this. ISTM arithmetic operators are essentially pure functions, and so the compiler may treat them as such. Stewart.
Sep 13 2009
Stewart Gordon wrote:Don wrote: <snip>The hardware dependency is still there, in every function which uses floating point. And the compiler must assume that dependency.Actually, you _can_ avoid changing the floating point settings in the course of an average program. But still....Floating point settings are just another case of the same thing, except that currently violations in relation to the former are allowed.There's a fundamental difference between them: the floating point settings are a hardware feature and it is IMPOSSIBLE to avoid them.Of course. There's nothing else to floating point, other than the built-in operators. And this is the problem with unconstrained rounding modes: they influence EVERYTHING.You can choose not to use locale settings. Or, you can pass them as a parameter, which doesn't work for floating point settings.As long as it's the built-in arithmetic operators you're concerned about.But that said, wrapping every arithmetic operation in a function to purify it of dependence on floating point settings wouldn't be ideal.It's worse than that. Every function which might call a function which might use floating point would have to pass the parameter. It doesn't work.No, they always depend on the FP settings, and assuming they are pure functions is not permitted in C, C++, or D. C allows the rounding mode to change at any time. This proposal: Rounding mode changes must only affect callees, not callers. The pure function memoisation issue: Certain functions may be marked as 'pure', even though they use floating point (and are therefore dependent on the FP state). The compiler must be able to prevent memoisation of those functions in cases where the rounding mode may have changed. (Or, equivalently, the memoisation must include the FP state). I've provided very simple solutions for both of these. I'm getting pretty irritated that people are acting as though these are difficult problems and trying to come up with convoluted solutions. Impose minimal semantics and it becomes trivial.Please do not get sidetracked on pure functions, it is orthogonal to this issue.I'm not sure about this. ISTM arithmetic operators are essentially pure functions, and so the compiler may treat them as such.
Sep 14 2009
Don wrote:The pure function memoisation issue: Certain functions may be marked as 'pure', even though they use floating point (and are therefore dependent on the FP state). The compiler must be able to prevent memoisation of those functions in cases where the rounding mode may have changed. (Or, equivalently, the memoisation must include the FP state).Don't forget that "uses floating point" is a transitive property. Any pure function that calls a pure-but-unmemoisable function is itself pure-but-unmemoisable. The "pure" annotation is now used for two related but different categories of functions with different properties, and the compiler needs some way to keep track of which is which across module boundaries. If the compiler can do that, then it can use the same mechanism to differentiate between pure and non-pure functions, and the "pure" annotation becomes redundant. Is there any real need to treat floating point state differently from other global state in regard to "pure"? -- Rainer Deyke - rainerd eldwood.com
Sep 14 2009
Rainer Deyke wrote:Don wrote:This is incorrect. You've got it backwards. It's not "uses floating point" that's the issue, it's "may use non-default floating point". A memoisable floating point function can only use default floating point. So, if it calls a non-memoisable pure function, it'll be using it in the default state (even though that function can accept non-default state). The transitivity propagates in the reverse direction: memoisability is transitive, non-memoisability is not. Non-memoisable functions cannot call memoisable ones without first ensuring that everything is back into the default state. The "pure" annotation is now used for twoThe pure function memoisation issue: Certain functions may be marked as 'pure', even though they use floating point (and are therefore dependent on the FP state). The compiler must be able to prevent memoisation of those functions in cases where the rounding mode may have changed. (Or, equivalently, the memoisation must include the FP state).Don't forget that "uses floating point" is a transitive property. Any pure function that calls a pure-but-unmemoisable function is itself pure-but-unmemoisable.related but different categories of functions with different properties, and the compiler needs some way to keep track of which is which across module boundaries. If the compiler can do that, then it can use the same mechanism to differentiate between pure and non-pure functions, and the "pure" annotation becomes redundant.It's true that you could provide a similar loophole for any specific global variable. But you'd need a *very* good reason.Is there any real need to treat floating point state differently from other global state in regard to "pure"?Yes. Because it's all-pervasive. Unless you treat it specially, you cannot use floating point in any pure function. That creates a restriction which is so severe that 'pure' becomes useless. Something like the following template function could not be pure: T add(T a, T b) { return a + b; } I do not believe that any other global state has an influence which is in the same league.
Sep 14 2009
Don wrote:Rainer Deyke wrote:Then I must have misunderstood your proposal. Either you did a poor job of explaining it, or I did a poor job of trying to understand it, or it's not as simple as you make it out to be. Since I'm not really interested in discussing the details of floating point rounding modes and memoisation, I'll just leave it at that.Don't forget that "uses floating point" is a transitive property. Any pure function that calls a pure-but-unmemoisable function is itself pure-but-unmemoisable.This is incorrect.It only affects floating point operations. Other pieces of global state (e.g. the global locale) affect other operations. Once you make a special case for floating point mode, it's easy to argue that the special case should be extended to other pieces of global data. A general solution generally beats special cases. Or maybe I'm completely overthinking this.Is there any real need to treat floating point state differently from other global state in regard to "pure"?Yes. Because it's all-pervasive.Unless you treat it specially, you cannot use floating point in any pure function. That creates a restriction which is so severe that 'pure' becomes useless. Something like the following template function could not be pure: T add(T a, T b) { return a + b; }In the presence of structs and class executing arbitrary code in opPlus, you can't make this pure anyway. Unless you force opPlus itself to be pure, which is almost certainly too restrictive for the current definition of pure. -- Rainer Deyke - rainerd eldwood.com
Sep 14 2009
Rainer Deyke wrote:Don wrote:Rainer Deyke wrote:Then I must have misunderstood your proposal. Either you did a poor job of explaining it, or I did a poor job of trying to understand it, or it's not as simple as you make it out to be.Don't forget that "uses floating point" is a transitive property. Any pure function that calls a pure-but-unmemoisable function is itself pure-but-unmemoisable.This is incorrect.Since I'm not really interested in discussing the details of floating point rounding modes and memoisation, I'll just leave it at that.The description I just gave of it was The thing you missed is that the non-memoisable pure functions can only read the global state.But you ALWAYS have a choice about that. You can rewrite functions which are independent of locale, or you can pass the locale as a parameter. With floating point, you don't have that option. BTW, global locales suck, big time. The idea that you can specify the formatting in the form of a locale is clearly the creation of someone who had never worked in an international environment. (And any locale which includes 'currency' is clearly the creation of an idiot). Once you make aIt only affects floating point operations. Other pieces of global state (e.g. the global locale) affect other operations.Is there any real need to treat floating point state differently from other global state in regard to "pure"?Yes. Because it's all-pervasive.special case for floating point mode, it's easy to argue that the special case should be extended to other pieces of global data.No, because this is a hardware special case. All the other cases can be dealt with in software.Or maybe I'm completely overthinking this.You're right. Bad example. But seriously, people give sqrt(x) as the classic example of a pure function.Unless you treat it specially, you cannot use floating point in any pure function. That creates a restriction which is so severe that 'pure' becomes useless. Something like the following template function could not be pure: T add(T a, T b) { return a + b; }In the presence of structs and class executing arbitrary code in opPlus, you can't make this pure anyway. Unless you force opPlus itself to be pure, which is almost certainly too restrictive for the current definition of pure.
Sep 15 2009
Don wrote:The thing you missed is that the non-memoisable pure functions can only read the global state.No, I saw that. But if a (pure) function calls another (pure) function that depends on global state, then the first (pure) function also depends on global state. int global_state; pure int f() { return global_state; } pure int g() { return f(); } int h() { global_state = 5; return g(); }But you ALWAYS have a choice about that. You can rewrite functions which are independent of locale, or you can pass the locale as a parameter. With floating point, you don't have that option.True. Unless you add functions/operators that use a fixed rounding mode to the language.BTW, global locales suck, big time. The idea that you can specify the formatting in the form of a locale is clearly the creation of someone who had never worked in an international environment. (And any locale which includes 'currency' is clearly the creation of an idiot).No argument there. -- Rainer Deyke - rainerd eldwood.com
Sep 15 2009
Rainer Deyke wrote:Don wrote:Ah. The important thing that makes this work is that the global FP state has a clearly defined default. (round-to-nearest, full precision, no floating point exceptions enabled). Part of the implicit contract of calling a 'memoisable pure' function is that the global FP state is in the default state. No pure function can change the state. So once you're in a memoisable pure function, you can safely call any non-memoisable pure function, in the knowledge that the default mode is in effect.The thing you missed is that the non-memoisable pure functions can only read the global state.No, I saw that. But if a (pure) function calls another (pure) function that depends on global state, then the first (pure) function also depends on global state. int global_state; pure int f() { return global_state; } pure int g() { return f(); } int h() { global_state = 5; return g(); }Yes, there's a few impractical ways it could be made to work (passing the rounding mode as an explicit parameter to every function is another option). Actually there's a related issue which I haven't mentioned: the floating point exception sticky flags are effectively a global variable, written to by every floating point operation which you perform. It can be covered in the same way: there's no guarantee that the flags will be correct in the presence of memoisation, so you need to be able to indicate to the compiler or runtime that the flags returned from this function call are important to you.But you ALWAYS have a choice about that. You can rewrite functions which are independent of locale, or you can pass the locale as a parameter. With floating point, you don't have that option.True. Unless you add functions/operators that use a fixed rounding mode to the language.
Sep 16 2009
Don wrote:Ah. The important thing that makes this work is that the global FP state has a clearly defined default. (round-to-nearest, full precision, no floating point exceptions enabled). Part of the implicit contract of calling a 'memoisable pure' function is that the global FP state is in the default state.Then I'm not sure it makes to treat "memoisable" as a property of a (pure) function. It seems more like a property of the context from which the function is called. *All* pure function are memoisable if you always call them with the floating point rounding mode set to the default. Conversely, all pure functions can be called with an arbitrary rounding mode if you treat them an unmemoisable. Some function - those that don't perform any floating point calculation, directly or indirectly - are independent from floating point state, and could even memoised even when called with an arbitrary floating point state. But there is no good way to automatically detect these functions. -- Rainer Deyke - rainerd eldwood.com
Sep 16 2009
Rainer Deyke wrote:Don wrote:That is correct.Ah. The important thing that makes this work is that the global FP state has a clearly defined default. (round-to-nearest, full precision, no floating point exceptions enabled). Part of the implicit contract of calling a 'memoisable pure' function is that the global FP state is in the default state.Then I'm not sure it makes to treat "memoisable" as a property of a (pure) function. It seems more like a property of the context from which the function is called. *All* pure function are memoisable if you always call them with the floating point rounding mode set to the default. Conversely, all pure functions can be called with an arbitrary rounding mode if you treat them an unmemoisable.Some function - those that don't perform any floating point calculation, directly or indirectly - are independent from floating point state, and could even memoised even when called with an arbitrary floating point state. But there is no good way to automatically detect these functions.It sounds as though you never saw my proposal. My proposal was that, by analogy to module(system), specific modules should be marked, for example: module (advancedfloatingpoint, system) std.math; or: module (nondefaultfloat, system) std.math; All pure functions are memoisable except for pure advancedfloatingpoint functions when called from a function (pure or not) which is also in an advancedfloatingpoint module. This is a very tiny hole in the 'purity' rules, and is a tiny addition to the language, but it's enough to cover the practical uses of floating-point rounding and exception flags.
Sep 17 2009
Don Wrote:I've provided very simple solutions for both of these. I'm getting pretty irritated that people are acting as though these are difficult problems and trying to come up with convoluted solutions. Impose minimal semantics and it becomes trivial.FWIW, I've liked your proposals, but I don't feel qualified to comment. I never adjust rounding modes. I do like how I can be ignorant and get code that is better optimized. You've shown deep knowledge of floating point operation in the past, and I'm inclined to accept reasonable-sounding proposals from those that know way more than me.
Sep 14 2009
On Mon, 14 Sep 2009 16:44:48 +0400, Jason House <jason.james.house gmail.com> wrote:Don Wrote:I second that. Nothing to add.I've provided very simple solutions for both of these. I'm getting pretty irritated that people are acting as though these are difficult problems and trying to come up with convoluted solutions. Impose minimal semantics and it becomes trivial.FWIW, I've liked your proposals, but I don't feel qualified to comment. I never adjust rounding modes. I do like how I can be ignorant and get code that is better optimized. You've shown deep knowledge of floating point operation in the past, and I'm inclined to accept reasonable-sounding proposals from those that know way more than me.
Sep 14 2009
Don wrote:PROPOSAL: Change the spec by adding the line to float.html: "If the floating-point rounding mode is changed within a function, it must be restored before the function exits. If this rule is violated (for example, by the use of inline asm), the rounding mode used for subsequent calculations is undefined."But that doesn't allow for changing the rounding mode at a global level, then rerunning one's computation to determine how rounding affects their final result.
Sep 11 2009
Walter Bright wrote:Don wrote:It doesn't prevent that at all. You just need to change the rounding mode before running your computation (at the start of main(), for example), and then reset it after performing the computation. What it does do is prevent different terms within a single expression from interacting which other. eg double foo() { return x() + y(); } x() and y() can use whichever rounding modes they like, and if they are impure, they can affect one another, but under this proposal, they cannot change the meaning of the addition inside foo().PROPOSAL: Change the spec by adding the line to float.html: "If the floating-point rounding mode is changed within a function, it must be restored before the function exits. If this rule is violated (for example, by the use of inline asm), the rounding mode used for subsequent calculations is undefined."But that doesn't allow for changing the rounding mode at a global level, then rerunning one's computation to determine how rounding affects their final result.
Sep 13 2009
On 2009-09-13 06:14:02 -0400, Don <nospam nospam.com> said:double foo() { return x() + y(); } x() and y() can use whichever rounding modes they like, and if they are impure, they can affect one another, but under this proposal, they cannot change the meaning of the addition inside foo().Problems still may occur if x and y do memoization however. You call x() with one rounding mode, it memoize the results, then call x() with another rounding mode and it reuses the previous results while it shouldn't. Basicaly, any memoization of x() should be discarded when you change the rounding mode. For instance the compiler should not reuse the value of x(), even if x is pure, in the following code: auto a = x(); setRoundingMode(...); auto b = x(); Basically, setRoundingMode should act as some sort of barrier for the optimizer, but how? -- Michel Fortin michel.fortin michelf.com http://michelf.com/
Sep 13 2009
Michel Fortin wrote:On 2009-09-13 06:14:02 -0400, Don <nospam nospam.com> said:Yes, but this is an orthogonal issue, which I have discussed previously.double foo() { return x() + y(); } x() and y() can use whichever rounding modes they like, and if they are impure, they can affect one another, but under this proposal, they cannot change the meaning of the addition inside foo().Problems still may occur if x and y do memoization however.Basicaly, any memoization of x() should be discarded when you change the rounding mode.Basically, setRoundingMode should act as some sort of barrier for the optimizer, but how?It's not a big deal, because rounding mode is changed so rarely, so you don't need to exactly catch all the cases where memoisation can safely be used; you only need to disallow all of the ones where it is unsafe. I proposed two options: (1) by analogy to SafeD, allow rounding mode and float exception flags to be respected only in specially marked modules ('module(advancedfloatingpoint)' or similar). Memoisation should be disabled in such modules. (As a further optimisation, the compiler can re-enable memoisation when such functions are called from non-advancedfloatingpoint modules). (2) provide a runtime call to disable memoisation. You'd be obliged to call this every time you changed the rounding mode or exception status from the default. I favour (1) because it provides huge optimisation potential for modules with default floating point. The point of all my proposals on this topic, is that these features cause a lot of trouble if they're completely unrestrained in the way that C/C++ does it, but the semantics required to completely domesticate them are not burdensome, and can be done without loss of useful functionality.
Sep 13 2009
Walter Bright wrote:Don wrote:Ok, I understand now. This makes sense.PROPOSAL: Change the spec by adding the line to float.html: "If the floating-point rounding mode is changed within a function, it must be restored before the function exits. If this rule is violated (for example, by the use of inline asm), the rounding mode used for subsequent calculations is undefined."But that doesn't allow for changing the rounding mode at a global level, then rerunning one's computation to determine how rounding affects their final result.
Sep 13 2009
Don wrote:PROPOSAL: Change the spec by adding the line to float.html: "If the floating-point rounding mode is changed within a function, it must be restored before the function exits. If this rule is violated (for example, by the use of inline asm), the rounding mode used for subsequent calculations is undefined."I added it into float.html for D2.
Sep 13 2009
Walter Bright:Don:An important purpose of a not bug-prone language is remove as many undefined situations as possible. If you add that to D2 specs, can the compiler catch at compile time all cases of violations to that rule? D has something for the actions that must be done before the function exits, the scope(exit). Can something like that be used to automatically avoid undefined rounding situations? Bye, bearophilePROPOSAL: Change the spec by adding the line to float.html: "If the floating-point rounding mode is changed within a function, it must be restored before the function exits. If this rule is violated (for example, by the use of inline asm), the rounding mode used for subsequent calculations is undefined."I added it into float.html for D2.
Sep 13 2009
bearophile wrote:An important purpose of a not bug-prone language is remove as many undefined situations as possible.That's right.If you add that to D2 specs, can the compiler catch at compile time all cases of violations to that rule?Unlikely. But that has to be weighed against the former behavior being defined in a useless way.D has something for the actions that must be done before the function exits, the scope(exit). Can something like that be used to automatically avoid undefined rounding situations?Using an RAII object to do it might be better.
Sep 13 2009
Walter Bright wrote:bearophile wrote:We could get close, I think. We could catch all violations in SafeD. Disallow calling core.stdc.fenv functions from inside SafeD modules, and provide an RAII object in std.math for changing the rounding mode. Actually there's probably a few other C functions we want to prohibit in SafeD -- the infamous string functions, for example. I think the goal should be try to remove undefined behaviour from SafeD. Long term goal: "If a program uses no system modules other than those provided in the standard library, it should not be exposed to undefined behaviour." ?An important purpose of a not bug-prone language is remove as many undefined situations as possible.That's right.If you add that to D2 specs, can the compiler catch at compile time all cases of violations to that rule?Unlikely. But that has to be weighed against the former behavior being defined in a useless way.I agree. I'd like to do something similar with the floating point exception state, too. Haven't worked out the details yet.D has something for the actions that must be done before the function exits, the scope(exit). Can something like that be used to automatically avoid undefined rounding situations?Using an RAII object to do it might be better.
Sep 14 2009
Don wrote:Walter Bright wrote:Yes, all the C functions will have to be gone through and separated. strlen() is safe, while strcpy() and printf() are not.bearophile wrote:We could get close, I think. We could catch all violations in SafeD. Disallow calling core.stdc.fenv functions from inside SafeD modules, and provide an RAII object in std.math for changing the rounding mode. Actually there's probably a few other C functions we want to prohibit in SafeD -- the infamous string functions, for example.An important purpose of a not bug-prone language is remove as many undefined situations as possible.That's right.If you add that to D2 specs, can the compiler catch at compile time all cases of violations to that rule?Unlikely. But that has to be weighed against the former behavior being defined in a useless way.I think the goal should be try to remove undefined behaviour from SafeD. Long term goal: "If a program uses no system modules other than those provided in the standard library, it should not be exposed to undefined behaviour." ?Right!
Sep 14 2009
Walter Bright:Yes, all the C functions will have to be gone through and separated. strlen() is safe, while strcpy() and printf() are not.For some of the removed functions better C-like ones can be provided: http://www.ddj.com/cpp/214502214 Bye, bearophile
Sep 14 2009
bearophile wrote:Walter Bright:Just use D instead.Yes, all the C functions will have to be gone through and separated. strlen() is safe, while strcpy() and printf() are not.For some of the removed functions better C-like ones can be provided: http://www.ddj.com/cpp/214502214 Bye, bearophile
Sep 14 2009
Walter Bright wrote:Don wrote:You sure? Does running beyond the bounds of the array if there's no null termination count as safe some how? :)We could get close, I think. We could catch all violations in SafeD. Disallow calling core.stdc.fenv functions from inside SafeD modules, and provide an RAII object in std.math for changing the rounding mode. Actually there's probably a few other C functions we want to prohibit in SafeD -- the infamous string functions, for example.Yes, all the C functions will have to be gone through and separated. strlen() is safe, while strcpy() and printf() are not.
Sep 14 2009
Brad Roberts wrote:Walter Bright wrote:Yes. Memory safety is defined as being free of memory corruption errors. Simply reading memory out of bounds does not corrupt memory. Note that it is ok for a memory safe program to generate a seg fault. printf() is not memory safe because of the %n format.strlen() is safe, while strcpy() and printf() are not.You sure? Does running beyond the bounds of the array if there's no null termination count as safe some how? :)
Sep 14 2009
Walter Bright wrote:Brad Roberts wrote:It does result in undefined behaviour, though. I don't see much difference. (Corrupting memory is a problem only because you read it again afterwards...)Walter Bright wrote:Yes. Memory safety is defined as being free of memory corruption errors. Simply reading memory out of bounds does not corrupt memory.strlen() is safe, while strcpy() and printf() are not.You sure? Does running beyond the bounds of the array if there's no null termination count as safe some how? :)Note that it is ok for a memory safe program to generate a seg fault.It'd be OK if it was guaranteed to generate a seg fault. But I don't think that's true here.printf() is not memory safe because of the %n format.
Sep 15 2009