It's bad if it alters values (e.g. rounding). Promotion from one number representation to another (as long as it preserves values) isn't bad. This is trickier than it might seem, but Virgil has a good take on this (https://github.com/titzer/virgil/blob/master/doc/tutorial/Nu...). Essentially, it only implicitly promotes values in ways that don't lose numeric information and thus are always reversible.
In the example, Virgil won't let you pass "1000.00" to an integer argument, but will let you pass "100" to the double argument.
Depends on what you want from such a hierarchy, of course, but there is for example an injection i32 -> f64 (and if you consider the i32 operations to be undefined on overflow, then it’s also a homomorphism wrt addition and multiplication). For a more general view, various Schemes’ takes on the “numeric tower” are informative.
Yes, you can ask the std::sto* functions for the position where they stopped because of invalid characters and see if that position is the end of the string, but that is much more complex than should be needed for something like that.
These functions don't convert a string to a number, they try to extract a number from a string. I would argue that most of the time, that's not what you want. Or at least, most of the time it's not what I need.
atoi has the same problem of course, but even worse.
// OK implicit promotions
def x1: i20;
def f1: float = x1;
def x2: i21;
def f2: float = x2;
def x3: i22;
def f3: float = x3;
def x4: i23;
def f4: float = x4;
// compile error!
def x5: i24;
def f5: float = x5; // requires rounding
This also applies to casts, which are dynamically checked. // runtime error if rounding alters value
def x5: i24;
def f5: float = float.!(x5);
https://doc.rust-lang.org/stable/rust-by-example/conversion/...
https://doc.rust-lang.org/stable/rust-by-example/conversion/...
If the conversion will always succeed (for example an 8-bit unsigned integer to a 32-bit unsigned integer), the From trait would be used to allow the conversion to feel implicit.
If the conversion could fail (for example a 32-bit unsigned integer to an 8-bit unsigned integer), the TryFrom trait would be used so that an appropriate error could be returned in the Result.
These traits prevent errors when converting between types and clearly mark conversions that might fail since they return Result instead of the output type.
To get the equivalent of Rust's
if let Ok(x) = input.parse::<i32>() {
println!("You entered {x}");
} else {
eprintln!("You did not enter a number");
}
you need something like: int x{};
auto [ptr, ec] = std::from_chars(input.data(), input.data() + input.size(), x);
if (ec == std::errc() && ptr == input.data() + input.size()) {
std::cout << "You entered " << x << std::endl;
} else {
std::cerr << "You did not enter a valid number" << std::endl;
}
I find the choice to always require a start and and end position, and not to provide a method that simply passes or fails, to be quite baffling. In C++26, they also added an automatic boolean conversion for from_chars' return type to indicate success, which considers "only consumed half the input from the start" to be a success.Maybe I'm weird for mostly writing code that does straightforward input-to-number conversions and not partial string parsers, but I have yet to see a good alternative for Rust's parse().