6 ms·
> > @jrose @fclc People who want NaN in their integers are welcome to do that in the privacy of their own bedrooms with new types they invent for this purpose.
by hoseja 3y ago
> > @jrose @fclc People who want NaN in their integers are welcome to do that in the privacy of their own bedrooms with new types they invent for this purpose.
And yet I bet they use IEEE754 floats without second thought.
- tialaramex 3y agoTypically in the unsafe languages under discussion they use the IEEE754 types without even a first thought. For example you can expect them to sort such types (you can define rules for sorting them, but if you just didn't in C too bad your program has Undefined Behaviour, while in C++ today it's even worse you've silently not written a C++ program at all and it never had any defined behaviour even before it tried to execute your invalid sort)
- gpderetta 3y agostd::sorting floats is perfectly well defined in C++ as long as you respect the precondition that there are no NaNs in your range. In other words the subset of floats without NaNs respects strict weak ordering. edit: when using the default comparison. With a custom comparator you can do whatever you want, including using std::weak_order to sort NaNs.
- tialaramex 3y agostd::sort is defined in terms of a semantic requirement Compare - a constraint on types - which forbids types which don't have Equivalence. Specifically, our type needs to have some equivalence predicate equiv(A, B) such that equiv(A, A) is true, and the provided comparisons for floating point types in C++ do not meet this requirement. I would be astonished if you're not correct at runtime in practice of course, but "It works in my environment" and "This is correct by the standard" are two different things.
- gpderetta 3y agostd::sort has a semantic requirement on the type of Compare, but Compare itself only has a constraint on the values it is comparing. From the 27.8.1.3 [1]: "comp shall induce a strict weak ordering on the values." It is not terribly clear what "values" is referring to, but form context it must be all values that are being sorted, as opposed to all possible values of T. The only explicit constrain on T is that it must be Move Assignable and Constructible. [1] https://eel.is/c++draft/alg.sorting.general#4 https://eel.is/c++draft/alg.sorting.general#4
- tialaramex 3y agoI see what you're saying about defining "values" as specifically only the values which, at runtime, we happen to be using Compare on, which is an idea I hadn't considered before - but I'm not sure how to square that with the way the rest of the language is defined. Maybe one day the proposed "Appendix" of all UB and IFNDR will actually explain clearly what the intended rules are. Corentin Jabot's "UB? In My Lexer?" showed that the ISO document is still very uh, unpolished after decades of effort.
- LegionMammal978 3y agoFrom [structure.requirements]/4 (at [0]): > Requirements are stated in terms of well-defined expressions that define valid terms of the types that meet the requirements. I'd read this as separating the values of a type into valid and invalid terms, where the valid terms can be any arbitrary set of values that's closed under the required operations. (This is explicitly clarified for concepts in [structure.requirements]/8, saying that the functions and conditions don't have to be total over all values of the type.) In this case, NaN would be considered an invalid term for std::sort(), so the compiler could break the program at a point only if it could statically guarantee that a NaN will inevitably make its way into std::sort(). [0] https://eel.is/c++draft/structure.requirements#4 https://eel.is/c++draft/structure.requirements#4
- tialaramex 3y agoStaring at that text just gives me a headache, in particular "This does not affect whether a type models the concept". I'm sure that everybody proposing and voting on this text was sure they agreed with it, however I'm doubtful that they all had the same meaning in mind, and I have no idea which one the author intended. What is "this" here? The example, of a type which clearly doesn't model the concept it syntactically conforms to? The requirement? All of the preceding text ?
- LegionMammal978 3y ago> What is "this" here? The example, of a type which clearly doesn't model the concept it syntactically conforms to? The requirement? All of the preceding text ? Generally, pronouns in the standard are scoped to the paragraph they're in. In this case, I read it as saying that the type (e.g., float) can still model a concept even if the semantic requirements are violated for some particular values. (That is, "this" is "whether the required operation is partial or total".) To support this reading, we can see from [res.on.requirements]/1 ([0]) that 'modeling' a concept is defined as meeting its associated semantic requirements: > A sequence Args of template arguments is said to model a concept C if Args satisfies C ([temp.constr.decl]) and meets all semantic requirements (if any) given in the specification of C. And 'meeting a semantic requirement' is mediated by [structure.requirements]/4, which I read to say that semantic requirements aren't universal rules for all values of a type, but necessary preconditions for values to be valid terms that can be passed into standard-library APIs. By that logic, even though an operation might not "meet the semantic requirements of [a] concept when operating on NaNs", it doesn't kill the entire type, it just rules out NaNs from being valid terms. I think the weirdness here is that the set of valid terms of any type is up in flux, to be defined by the ultimate user. But even if the operations don't meet the semantics over that entire set, the compiler has no way to prove that the program is ill-formed until the user actually generates one of the offending values, asserting that it truly is a valid term. [0] https://eel.is/c++draft/res.on.requirements#1 https://eel.is/c++draft/res.on.requirements#1
- eru 3y agoCould you explain the difference between C and C++ here? I thought they deal with undefined behaviour in approximately the same way?
- tialaramex 3y agoMy argument is that what's happening here in C++ is not merely Undefined Behaviour. UB is a runtime thing, at runtime your program tried to do this operation we said mustn't ever happen, so all bets are off. C++ also has this idea of programs which are Ill-Formed, No Diagnostic Required, meaning that this was never a C++ program, the ISO document doesn't tell you what this program will do, and a compiler isn't expected to tell you that you didn't write a C++ program in these cases either, so too bad but it's not our fault. IFNDR is there because these are non-trivial semantic properties, so by Rice's Theorem they're Undecidable, the compiler cannot be sure if every arbitrary program meets the semantic requirement. The ISO C++ document would need to document a check which can be performed, allowing compilers to accept only programs which pass that check, or something similar giving enough rope for them to avoid cases where your program compiles in Clang but not MSVC or vice versa, that sounds awkward to write, instead it just YOLOs the whole problem with IFNDR -- if any program unknowingly does not meet these requirements it was silently never a C++ program anyway, not our fault it doesn't work. As it has grown over the decades, C++ has added more, and more, and more IFNDR to the text, because it sure is easier to wave away such programs as "That isn't C++" than to either insist compiler vendors write more diagnostics (Boring! Expensive!) or define the language more carefully so as to make the wrong programs impossible to write. They do not have a comprehensive list of IFNDR (nor UB) in their language today. My belief (and some well known C++ people agree) is that the vast majority of real world C++ programs today are Ill-Formed by these definitions and so do not have and never had any meaning by the text of the standard. I think this is substantially worse than the situation in C.
- eru 3y ago> UB is a runtime thing, [...] Compare and contrast https://stackoverflow.com/a/11546378 https://stackoverflow.com/a/11546378 > FNDR is there because these are non-trivial semantic properties, so by Rice's Theorem they're Undecidable, the compiler cannot be sure if every arbitrary program meets the semantic requirement. As pointed out elsewhere, Rice's theorem does not force this choice. You could also be conservative: insist on only disallowing all 'bad' programs at the price of banning some 'good' programs. C++'s type system already disallows plenty of 'good' programs anyway. > My belief (and some well known C++ people agree) is that the vast majority of real world C++ programs today are Ill-Formed by these definitions and so do not have and never had any meaning by the text of the standard. I think this is substantially worse than the situation in C. Thanks! That's the kind of answer I was trying to get!
- wruza 3y agoI agree with the quote and use IEEE754 floats with second thought every time. In my perfect world floats don’t have inf, nan and base-2 exponent. And integers are 0-sized exponent floats.
- temac 3y ago> In my perfect world floats don’t have inf, nan and base-2 exponent I'm quite unsure what do you want as floats then?
- Symmetry 3y agoDecimal floating point is a standard that some people use and which even has hardware support in some IBM cores. https://en.wikipedia.org/wiki/Decimal_floating_point https://en.wikipedia.org/wiki/Decimal_floating_point
- eru 3y agoYou can do that, but I think for most applications where you want decimal, you can get away with fixed point computation? Eg if you want to calculate with money, just specify everything in terms of cents (or 0.001 of a cent or whatever), and then use an integer data type. Perhaps even an unlimited size integer data type.
- afiori 3y agoI feel like at some point the performance hit of using actual rationals (pairs of bignums) makes sense
- adgjlsfhk1 3y agoThe downside of rationals is that they degrade extremely quickly as soon as you take a sqrt or do trig. Decimal floating point has the advantage of losing accuracy in the places people are used to.