| Home | Libraries | People | FAQ | More

### Constructing and Interconverting Between Number Types

All of the number types that are based on `number` have certain conversion rules in common. In particular:

- Any number type can be constructed (or assigned) from any fundamental (built-in) arithmetic type, as long as the conversion isn't lossy (for example float to int conversion):

      cpp_dec_float_50 df(0.5);   // OK construction from double
      cpp_int          i(450);    // OK constructs from signed int
      cpp_int          j = 3.14;  // Error, lossy conversion.

- A number can be explicitly constructed from an arithmetic type, even when the conversion is lossy:

      cpp_int          i(3.14);       // OK explicit conversion
      i = static_cast<cpp_int>(3.14)  // OK explicit conversion
      i.assign(3.14);                 // OK, explicit assign and avoid a temporary from the cast above
      i = 3.14;                       // Error, no implicit assignment operator for lossy conversion.
      cpp_int          j = 3.14;      // Error, no implicit constructor for lossy conversion.

- A `number` can be converted to any fundamental (built-in) type, via the `convert_to` member function:

      mpz_int z(2);
      int i = z.convert_to<int>(); // sets i to 2

- Conversions to rational numbers from floating-point ones are always allowed, and are exact and implicit as long as the rational number uses an unbounded integer type. Please be aware that constructing a rational number from an extended precision floating-point type with a large exponent range can effectively run the system out of memory, as in the extreme case 2max_exponent / CHAR_BITS bytes of storage may be required. This does not represent a problem for fundamental (built-in) floating-point types however, as the exponent range for these is rather limited.
- Conversions to floating-point numbers from rational ones are rounded to nearest (less than 0.5 Unit in the last place (ULP) error) as long as the floating-point number is binary, and the integer type used by the rational number is unbounded.

Additional conversions may be supported by particular backends.

- A `number` can be converted to any fundamental (built-in) type, via an explicit conversion operator: this functionality is only available on compilers supporting C++11's explicit conversion syntax.

      mpz_int z(2);
      int i = z;                     // Error, implicit conversion not allowed.
      int j = static_cast<int>(z);   // OK explicit conversion.

- Any number type can be explicitly constructed (or assigned) from a `const char*` or a `std::string`:

      // pi to 50 places from a string:
      cpp_dec_float_50 df("3.14159265358979323846264338327950288419716939937510");
      // Integer type will automatically detect "0x" and "0" prefixes and parse the string accordingly:
      cpp_int          i("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000000");
      // Invalid input always results in a std::runtime_error being thrown:
      i = static_cast<cpp_int>("3.14");
      // implicit conversions from strings are not allowed:
      i = "23"; // Error, no assignment operator for implicit conversion from string
      // assign member function, avoids having to create a temporary via a static_cast:
      i.assign("23");  // OK

- Any number type will interoperate with the fundamental (built-in) types in arithmetic expressions as long as the conversions are not lossy:

      // pi to 50 places from a string:
      cpp_dec_float_50 df = "3.14159265358979323846264338327950288419716939937510";
      // Multiply by 2 - using an integer literal here is usually more efficient
      // than constructing a temporary:
      df *= 2;

      // You can't mix integer types with floats though:
      cpp_int i = 2;
      i *= 3.14;  // Error, no *= operator will be found.

- Any number type can be streamed to and from the C++ iostreams:

      cpp_dec_float_50 df = "3.14159265358979323846264338327950288419716939937510";
      // Now print at full precision:
      std::cout << std::setprecision(std::numeric_limits<cpp_dec_float_50>::max_digits10)
         << df << std::endl
      cpp_int i = 1;
      i <<= 256;
      // Now print in hex format with prefix:
      std::cout << std::hex << std::showbase << i << std::endl;

- Interconversions between number types of the same family are allowed and are implicit conversions if no loss of precision is involved, and explicit if it is:

      int128_t     i128 = 0;
      int266_t     i256 = i128;  // OK implicit widening conversion
      i128_t            = i256;  // Error, no assignment operator found, narrowing conversion is explicit.
      i128_t            = static_cast<int128_t>(i256); // OK, explicit narrowing conversion.

      mpz_int      z    = 0;
      mpf_float    f    = z;    // OK, GMP handles this conversion natively, and it's not lossy and therefore implicit.

      mpf_float_50 f50  = 2;
      f                 = f50;  // OK, conversion from fixed to variable precision, f will have 50 digits precision.
      f50               = f;    // Error, conversion from variable to fixed precision is potentially lossy, explicit cast required.

- Some interconversions between number types are completely generic, and are always available, albeit the conversions are always explicit:

      cpp_int cppi(2);
      // We can always convert between numbers of the same category -
      // int to int, rational to rational, or float to float, so this is OK
      // as long as we use an explicit conversion:
      mpz_int z(cppi);
      // We can always promote from int to rational, int to float, or rational to float:
      cpp_rational     cppr(cppi);  // OK, int to rational
      cpp_dec_float_50 df(cppi);    // OK, int to float
      df                  = static_cast<cpp_dec_float_50>(cppr);  // OK, explicit rational to float conversion
      // However narrowing and/or implicit conversions always fail:
      cppi                =   df;    // Compiler error, conversion not allowed

- Other interconversions may be allowed as special cases, whenever the backend allows it:

      mpf_t     m;           // Native GMP type.
      mpf_init_set_ui(m, 0); // set to a value;
      mpf_float i(m);        // copies the value of the native type.

More information on what additional types a backend supports conversions from are given in the tutorial for each backend. The converting constructor will be implicit if the backend's converting constructor is also implicit, and explicit if the backends converting constructor is also explicit.