abseil / abseil-cpp

Abseil Common Libraries (C++)
https://abseil.io
Apache License 2.0
15.05k stars 2.62k forks source link

[Question] how can we convert an uint128_t to a string an an string to a uint128_t #363

Open Milerius opened 5 years ago

Milerius commented 5 years ago

Hello after reading: https://github.com/abseil/abseil-cpp/blob/master/absl/strings/numbers.h

I think i misunderstood how can i convert an uint128_t to a string like std::stoull will do

Milerius commented 5 years ago

For example i have this snippet of code for the moment:

st_price generate_st_price_from_api_price(std::string price_str) noexcept
    {
        price_str.erase(std::find(price_str.begin(), price_str.end(), '.'));
        ltrim(price_str, "0");
        //! I would like to replace std::stoull to an equivalent 
        //! with abseil for converting my string to an uint128_t
        return st_price{std::stoull(price_str)};
    }

and the equivalent of:

auto string_value = std::to_string(my_integer_128_value);
derekmauro commented 5 years ago

I looked and I don't think we provide a simple way to convert between std::string and absl::uint128. I might be missing it too if it is there.

I do see two ways to convert from absl::uint128 to std::string, but neither is terribly nice:

#include <string>
#include <sstream>

#include "absl/numeric/int128.h"
#include "absl/strings/str_format.h"

std::string f(absl::uint128 x) {
  return absl::StrFormat("%d", x);
}

std::string g(absl::uint128 x) {
  std::stringstream ss;
  ss << x;
  return ss.str();
}

I don't see any way to convert from std::string to absl::uint128 at all (without rolling it yourself).

@mbxx - Can you check why absl/strings/numbers.h includes absl/numeric/int128.h? It is probably a result of something that got removed.

We should also consider supporting a nicer way to do this.

Milerius commented 5 years ago

I manage to do it already :

 std::string get_price_as_string_decimal(const mmbot::config &cfg, const st_symbol &symbol, st_price price) noexcept
    {
        std::stringstream ss;
        ss << price.value();
        std::string price_str;
        ss >> price_str;
        return unformat_str_to_representation_price(cfg, symbol, price_str);
    }

And for converting 2ints 64 i use:

//! Take first 20 digit, use std::stoull and then add the other digits, should not be > 40 digits i believe
absl::uint128 value = std::stoull(price_str.substr(0, 20));
        auto low_str = price_str.substr(20);
        for (std::size_t i = 0; i < low_str.size(); ++i) {
            value *= 10;
        }
        value += std::stoull(low_str);

I dont have a better idea for a string to uint128_t in my case

Milerius commented 5 years ago

is StrFormat better than my trick ?

Milerius commented 5 years ago

Otherwise it"s a really nice library with great cmake integration and target, not intruisive and friendly, thank's a lot

mbxx commented 5 years ago

@derekmauro: There are routines for converting back and forth between absl::uint128 and hex strings that got dropped from the release. Nothing for decimal strings. This is definitely not ideal. We should consider adding absl::uint128 support to StrCat and SimpleAtoi. I haven't thought it through deeply, but I don't see any problems with that.

@Milerius: Thank you for the kind words!

StrFormat will likely be slightly faster for converting a uint128 into a string than using streaming, but both are completely fine. I would just match the pattern of what you're already doing. If you use StrFormat for other things, use StrFormat for uint128 as well. If you use streaming output for other things, use it for uint128 as well.

Your technique for converting a string into a uint128 has the right idea, but taking the first 20 digits is too many because std::numeric_limits<uint64_t>::max() is 18446744073709551615, so there are some 20 digit numbers that can't be represented in a uint64_t.

So when I run this program:

#include <iostream>
#include <limits>

int main()
{
    std::cout << std::numeric_limits<uint64_t>::max() << "\n";
    std::cout << std::stoull("99999999999999999999") << "\n";
}

it crashes with this error message:

terminate called after throwing an instance of 'std::out_of_range'
18446744073709551615
  what():  stoull

Personally, I would turn a string into a uint128 by pull digits off of the string one at a time until I knew that I needed something more efficient. At that point, I'd consider handling multiple digits at a time, as you're doing here.

Milerius commented 5 years ago

@mbxx Oh i see, we can probably write an efficient function absl::stouint128(std::string) no ?

Milerius commented 5 years ago

The writing of financial software requires us to pass floating numbers so often we use strings that represents huge floating numbers by converting them to very large integer, that's why I needed uint128_t of absl

mbxx commented 5 years ago

@Milerius: In the longer term, Abseil will add some functions to handle this efficiently. In the short term, I'm afraid you'd have to write your own function.

I should point out that if you don't need your code to be portable and you're using Clang or GCC as your compiler, both of those compilers have 128 bit integer types as extensions. You may be able to use one of those.

Milerius commented 5 years ago

@mbxx Thank's but unfortunately i need to be portable !

mbxx commented 5 years ago

@Milerius: I was afraid you'd say that.

Adding support for converting uint128 to and from strings is definitely something we need to do. It may take us a little while to get to it, since we're stretched a bit thin, and I apologize for that. I'll keep this issue open to track the feature request.

jorgbrown commented 5 years ago

StrCat doesn't support uint128 currently because it does everything using AlphaNum buffers, and those buffers aren't large enough to support all the digits of a uint128.

uint128 does know how to stream itself, however, and StrJoin support streamable things, so you can do this:

std::string str = absl::StrJoin(std::make_tuple(ui), "", absl::StreamFormatter());

It generates reasonable code, too => https://godbolt.org/z/zMnK57

I'm working on a change right now to support Parsing a string into a uint128 using SimpleAtoi.

cpsusie commented 3 years ago

@jorgbrown

I'm working on a change right now to support Parsing a string into a uint128 using SimpleAtoi.

Did you ever get around to have std::string -> uint128 parsing supported by SimpleAtoi?