[HN Gopher] Integer Conversions and Safe Comparisons in C++20
___________________________________________________________________
Integer Conversions and Safe Comparisons in C++20
Author : ibobev
Score : 45 points
Date : 2022-09-12 09:51 UTC (1 days ago)
(HTM) web link (www.cppstories.com)
(TXT) w3m dump (www.cppstories.com)
| kwant_kiddo wrote:
| Once again the comments prove that people are more interested in
| trash-talking C++ than than solving real problems.
|
| C++20 IS fixing many complaints or design-choices made in the
| past.
|
| In terms of the article it casually mentions in the end using
|
| '''-Werror -Wall -Wextra''' The unsigned signed comparisons will
| fail to compile. Most serious projects use these flags anyway.
|
| I wonder if clang-tidy will also catch this?
| [deleted]
| synergy20 wrote:
| I'm fully aware of pitfalls of c++ (and c), but I keep coming
| back to them as all the rest alternatives failed to provide the
| same speed, performance, and small size.
|
| every language has its dark corner, none is perfect, same as C
| and C++. C and C++ are popular for decades is a fact, like it
| or not, data does not lie.
| anonymoushn wrote:
| > Most serious projects use these flags anyway.
|
| Most serious projects are opting in to being unable to compile
| on future compilers whenever new warnings are added?
| phoe-krk wrote:
| As a person working on big C++ telco codebases for a living,
| I'm fully aware that C++ is solving real problems and that
| -Weverything makes it somewhat possible to work with the
| language. At the same time, I'm appalled by the negative
| consequences of the "let's fix problems with the old and broken
| comparison operator by introducing a new, better comparison
| operator, since we need to maintain almost complete backwards
| compatibility" approach that C++ takes here.
|
| I'm interested in solving real problems, but C++ deserves trash
| talking for this reason alone.
| rictic wrote:
| What's the alternative? Change the semantics of the
| comparison operator so that code looks cleaner? That means
| that when you read a comparison in source you have to know
| how it's going to be compiled in order to tell if the code is
| correct or not.
| lifthrasiir wrote:
| You can opt in a new behavior, attribute syntax added in
| C++11 should make this much more bearable. In fact, I'm
| honestly surprised that this mode of language evolution
| didn't seem to be frequently discussed in C++ WG at all;
| ECMAScript had `use strict` which is very much the same
| thing and well received, and if ECMAScript can do it with a
| lot of legacy code, there is no real reason C++ can do the
| same.
| titzer wrote:
| This isn't that, though. C/C++ continue to leave
| undefined behavior in _specifically_ for performance. It
| would, AFAICT, be standard-compliant behavior for a
| compiler to handled mixed-sign comparisons involving
| negative numbers in the intuitive number-line way, but
| the language specification doesn 't require this because
| it means another comparison[1]. Because performance, it's
| the programmer's responsibility to avoid having negative
| numbers in such comparisons. And woe to they who have
| bugs in their programs, the compiler may even exploit
| that UB to assume the code is never reached. The default
| is all wrong because the priorities are all wrong.
|
| [1] It's non-obvious to me why an implicit conversion
| with potential UB would _ever_ be a good idea. If a
| programmer really did want a UB-having single-machine
| compare between a signed (but non-negative) int and an
| unsigned int, they could write an explicit static_cast
| and then compare. Such a static_cast would be the UB-
| having nop that unlocked a non-UB comparison. You 'd get
| the exact machine code that you wanted, but you had to
| opt into UB. Bad default that the short, intuitive-
| looking code has UB.
| anonymoushn wrote:
| I don't think this is correct. Comparison operators are
| defined to perform the usual arithmetic conversions
| before they are applied, and the usual arithmetic
| conversions for converting to unsigned integer types are
| defined like so:
|
| > If the destination type is unsigned, the resulting
| value is the least unsigned integer congruent to the
| source integer (modulo 2n where n is the number of bits
| used to represent the unsigned type). [Note: In a two's
| complement representation, this conversion is conceptual
| and there is no change in the bit pattern (if there is no
| truncation). ]
| titzer wrote:
| Converting a signed negative number to unsigned is UB.
| anonymoushn wrote:
| Can you provide a citation? I have above quoted the part
| of the standard that defines the value of the result of
| the conversion.
| sharikous wrote:
| I don't know why they don't do that either. Perhaps
| because a transition like that will take multiple decades
| of consistent and steady guidance to succeed, given the
| nature and size of C++ codebases and nobody will take the
| responsibility for it?
| bruce343434 wrote:
| Welcome to every language that has the balls to say "yeah
| you know what, that didn't work, here's a revision". Stuff
| is deprecated all the time.
| lultimouomo wrote:
| The usual arithmetic conversions linked from the article [0] are
| a bit different from the ones I know. In particular, if int can
| represent all values of all operands, both of them will be
| converted to int.
|
| Thus comparing unsigned short and short will usually (if short is
| narrower than int) do the right thing. C++Insights (which I
| didn't know! This is the real pearl of this article.) agrees with
| me [1].
|
| [0]
| https://en.cppreference.com/w/cpp/language/operator_arithmet...
|
| [1]
| https://cppinsights.io/lnk?code=I2luY2x1ZGUgPGNzdGRpbz4KCmlu...
| anonymoushn wrote:
| The relevant part of your first link seems to be right at the
| beginning:
|
| > If the operand passed to an arithmetic operator is integral
| or unscoped enumeration type, then before any other action (but
| after lvalue-to-rvalue conversion, if applicable), the operand
| undergoes integral promotion.
|
| Clicking "integral promotion" leads here:
| https://en.cppreference.com/w/cpp/language/implicit_conversi...
| titzer wrote:
| Virgil has a family of completely well-defined (i.e. no UB)
| fixed-size integer types with some hard-fought rules that I
| eventually got around to documenting here:
|
| https://github.com/titzer/virgil/blob/master/doc/tutorial/Fi...
|
| One of the key things is that values are never silently truncated
| (other than 2's-complement wrap-around that is built-in to
| arithmetic) or values changed; only promotions. The only sane
| semantics for over-shifts (shifts larger than the size of the
| type) is to shift the bits out, like a window.
|
| The upshot of all that is that Virgil has a pretty sane semantics
| for fixed-size integers, IMHO. Particularly comparisons.
| Comparing signed and unsigned ints is not UB and cannot silently
| give the wrong answer; integers are always compared on the same
| number line. It takes only a maximum of one extra comparison to
| achieve this. AFAICT this is what achieved by the std::cmp*
| referenced in the article.
| phoe-krk wrote:
| Looking at horror stories like this along with their "solutions"
| that are done by slapping more standard functions on top of the
| already existing piles, I'm glad that I do my personal
| programming in languages which 1) have meaningful numeric towers
| and automatically do promotion/demotion as appropriate, 2) don't
| implicitly convert signed integers into unsigned integers and
| error out instead.
|
| With each standard revision, C++ is becoming more and more
| horrible as much as it strives to become a better and more useful
| language.
| GeorgeTirebiter wrote:
| Pray tell, what are your personal programming languages?
| phoe-krk wrote:
| Lisp and Erlang. The latter doesn't have a numeric tower but
| has automatic bigint conversion and handling. The former is
| eager to error upon signed/unsigned type violations once you
| specify them.
| jcelerier wrote:
| > has automatic bigint conversion
|
| how can one implement this without a branch before
| literally any arithmetic operation?
| throwawaymaths wrote:
| You're not using Erlang for speed. You're using Erlang
| for reliability. (Note this is not the same thing as
| correctness or "safety")
| phoe-krk wrote:
| In CL, you can declare that you need modulo arithmetic,
| which will give you branchless arithmetic operations.
|
| In Erlang, you don't; the language was never made for
| that sort of speed.
| AlexSW wrote:
| Not sure Erlang is strong on the basic types front on the
| whole though; I've never been a fan of strings in Erlang.
| Greatly prefer C++ on this front.
___________________________________________________________________
(page generated 2022-09-13 23:02 UTC)