mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-11-25 09:02:00 +00:00
Make reading of Decimal more compatible with other DBMS
This commit is contained in:
parent
7b2b726c17
commit
408fc241d6
@ -134,3 +134,14 @@ inline __int128 exp10_i128(int x)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/// intExp10 returning the type T.
|
||||
template <typename T>
|
||||
inline T intExp10OfSize(int x)
|
||||
{
|
||||
if constexpr (sizeof(T) <= 8)
|
||||
return intExp10(x);
|
||||
else
|
||||
return common::exp10_i128(x);
|
||||
}
|
||||
|
@ -31,8 +31,13 @@ template <> struct FunctionUnaryArithmeticMonotonicity<NameIntExp10>
|
||||
static bool has() { return true; }
|
||||
static IFunction::Monotonicity get(const Field & left, const Field & right)
|
||||
{
|
||||
Float64 left_float = left.isNull() ? -std::numeric_limits<Float64>::infinity() : applyVisitor(FieldVisitorConvertToNumber<Float64>(), left);
|
||||
Float64 right_float = right.isNull() ? std::numeric_limits<Float64>::infinity() : applyVisitor(FieldVisitorConvertToNumber<Float64>(), right);
|
||||
Float64 left_float = left.isNull()
|
||||
? -std::numeric_limits<Float64>::infinity()
|
||||
: applyVisitor(FieldVisitorConvertToNumber<Float64>(), left);
|
||||
|
||||
Float64 right_float = right.isNull()
|
||||
? std::numeric_limits<Float64>::infinity()
|
||||
: applyVisitor(FieldVisitorConvertToNumber<Float64>(), right);
|
||||
|
||||
if (left_float < 0 || right_float > 19)
|
||||
return {};
|
||||
|
@ -1,4 +1,5 @@
|
||||
#include <IO/ReadHelpers.h>
|
||||
#include <Common/intExp.h>
|
||||
|
||||
|
||||
namespace DB
|
||||
@ -80,21 +81,33 @@ inline bool readDigits(ReadBuffer & buf, T & x, uint32_t & digits, int32_t & exp
|
||||
++places; // num zeroes before + current digit
|
||||
if (digits + places > max_digits)
|
||||
{
|
||||
if constexpr (_throw_on_error)
|
||||
throw Exception("Too many digits (" + std::to_string(digits + places) + " > " + std::to_string(max_digits)
|
||||
+ ") in decimal value", ErrorCodes::ARGUMENT_OUT_OF_BOUND);
|
||||
return false;
|
||||
if (after_point)
|
||||
{
|
||||
/// Simply cut excessive digits.
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
if constexpr (_throw_on_error)
|
||||
throw Exception("Too many digits (" + std::to_string(digits + places) + " > " + std::to_string(max_digits)
|
||||
+ ") in decimal value", ErrorCodes::ARGUMENT_OUT_OF_BOUND);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
digits += places;
|
||||
if (after_point)
|
||||
exponent -= places;
|
||||
|
||||
digits += places;
|
||||
if (after_point)
|
||||
exponent -= places;
|
||||
// TODO: accurate shift10 for big integers
|
||||
x *= intExp10OfSize<T>(places);
|
||||
places = 0;
|
||||
|
||||
// TODO: accurate shift10 for big integers
|
||||
for (; places; --places)
|
||||
x *= 10;
|
||||
x += (byte - '0');
|
||||
break;
|
||||
x += (byte - '0');
|
||||
break;
|
||||
}
|
||||
}
|
||||
case 'e': [[fallthrough]];
|
||||
case 'E':
|
||||
@ -144,10 +157,12 @@ inline void readDecimalText(ReadBuffer & buf, T & x, uint32_t precision, uint32_
|
||||
digits, x, exponent, scale, precision), ErrorCodes::ARGUMENT_OUT_OF_BOUND);
|
||||
|
||||
if (static_cast<int32_t>(scale) + exponent < 0)
|
||||
throw Exception(fmt::format(
|
||||
"Decimal value has too large number of digits after point: {} digits were read: {}e{}."
|
||||
" Expected to read decimal with scale {} and precision {}",
|
||||
digits, x, exponent, scale, precision), ErrorCodes::ARGUMENT_OUT_OF_BOUND);
|
||||
{
|
||||
/// Too many digits after point. Just cut off excessive digits.
|
||||
x.value /= intExp10OfSize<T>(-exponent - scale);
|
||||
scale = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
scale += exponent;
|
||||
}
|
||||
|
@ -0,0 +1,14 @@
|
||||
1.10000
|
||||
1.12345
|
||||
1.12345
|
||||
1.12345
|
||||
1.12345
|
||||
1.12345
|
||||
12345.10000
|
||||
123456789123.10000
|
||||
1234567891234.10000
|
||||
1234567891234.12345
|
||||
1.12345
|
||||
1.12345
|
||||
1.12344
|
||||
1.12345
|
@ -0,0 +1,24 @@
|
||||
SELECT CAST('1.1' AS Decimal(10, 5));
|
||||
SELECT CAST('1.12345' AS Decimal(10, 5));
|
||||
SELECT CAST('1.123451' AS Decimal(10, 5));
|
||||
SELECT CAST('1.1234511111' AS Decimal(10, 5));
|
||||
SELECT CAST('1.12345111111' AS Decimal(10, 5));
|
||||
SELECT CAST('1.12345111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111' AS Decimal(10, 5));
|
||||
SELECT CAST('12345.1' AS Decimal(10, 5));
|
||||
|
||||
-- Actually our decimal can contain more than 10 digits for free.
|
||||
SELECT CAST('123456789123.1' AS Decimal(10, 5));
|
||||
SELECT CAST('1234567891234.1' AS Decimal(10, 5));
|
||||
SELECT CAST('1234567891234.12345111' AS Decimal(10, 5));
|
||||
-- But it's just Decimal64, so there is the limit.
|
||||
SELECT CAST('12345678912345.1' AS Decimal(10, 5)); -- { serverError 69 }
|
||||
|
||||
-- The rounding may work in unexpected way: this is just integer rounding.
|
||||
-- We can improve it but here is the current behaviour:
|
||||
SELECT CAST('1.123455' AS Decimal(10, 5));
|
||||
SELECT CAST('1.123456' AS Decimal(10, 5));
|
||||
SELECT CAST('1.123445' AS Decimal(10, 5)); -- Check if suddenly banker's rounding will be implemented.
|
||||
|
||||
CREATE TEMPORARY TABLE test (x Decimal(10, 5));
|
||||
INSERT INTO test VALUES (1.12345111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111);
|
||||
SELECT * FROM test;
|
Loading…
Reference in New Issue
Block a user