не правильно работает округление с Double Precision
Модератор: kdv
не правильно работает округление с Double Precision
У меня в базе есть UDF:
**************
function R_RoundTo(aValue: PDouble; aDigit: PInteger): Double; cdecl; export;
begin
Result := RoundTo(aValue^, aDigit^);
end;
**************
Если при передаче параметров тип переменной отличный от Numeric, то округление происходит неверно - а именно число на входе в процедуру приводится к числу с тремя знаками, то есть указываем в хранимой процедуре просто R_ROundTo(20.8335, -3) - все работает - округляет 20.834, если сделать то же через переменную:
Value Double Precision
...
Value = 20.8335;
...R_RountTo(Value, -3)...
...
Уже не будет работать - выдаст 20.833 и если константа смешивается с переменной тоже не работает - только если все выражение преобразовать к Numeric(15,10) , все это имеет место как ч понял только в хранимых процедурах - если вызывать функцию просто в запросе, то работает даже если передаваемый параметр заведомо преобразовать к Double Precision
Что делать??? У меня база в которой уже ведется работа и там много полей Double Precision а не Numeriс!!!
**************
function R_RoundTo(aValue: PDouble; aDigit: PInteger): Double; cdecl; export;
begin
Result := RoundTo(aValue^, aDigit^);
end;
**************
Если при передаче параметров тип переменной отличный от Numeric, то округление происходит неверно - а именно число на входе в процедуру приводится к числу с тремя знаками, то есть указываем в хранимой процедуре просто R_ROundTo(20.8335, -3) - все работает - округляет 20.834, если сделать то же через переменную:
Value Double Precision
...
Value = 20.8335;
...R_RountTo(Value, -3)...
...
Уже не будет работать - выдаст 20.833 и если константа смешивается с переменной тоже не работает - только если все выражение преобразовать к Numeric(15,10) , все это имеет место как ч понял только в хранимых процедурах - если вызывать функцию просто в запросе, то работает даже если передаваемый параметр заведомо преобразовать к Double Precision
Что делать??? У меня база в которой уже ведется работа и там много полей Double Precision а не Numeriс!!!
!!!!!!!
Я ЖЕ СКАЗАЛ НА ВХОДЕ В ПРЦЕДУРУ - КАК ОНА РАБОТАЕТ В ДЕЛФИ ЭТО УЖЕ ВТОРОЙ ВОПРОС СЕЙЧАС Я ДЕЛФИ НАПИСАЛ СВОЮ ПРОЦЕДУРУ, НО ЕСЛИ ПРИ ПЕРЕДАЧЕ ПАРАМЕТРОВ В ХРАНИМОЙ ПРОЦЕДУРЕ ИСПОЛЬЗОВАТЬ НЕ ЧИСЛОВЫЕ КОНСТАНТЫ И НЕ NUMERIC А НАПРИМЕР КОНСТАНТЫ СМЕШАННЫЕ С NUMERIC ИЛИ СМЕШАННЫЕ С DOUBLE PRECISION ИЛИ ПРОСТО DOUBLE PRECISION - то уже на входе в процедуру число неправильно округлено до тысячных - какого хрена оно там окгругляется вообще непонятно!!! Сейчас я везде в ХП перед передачей в процедуру округления присваиваю выражение которое нужно округлить в переменную NUMERIC - но это ж костыль а не панацея!! ЕЩЕ РАЗ ПОВТОРЯЮ - НА ВХОДЕ В ПРЦЕДУРУ ДЕЛЬФЕ ПЕРЕМЕННАЯ ПОПАДАЕТ ОКРУГЛЕННОЙ!!! ВОПРОС - КАКОГО ХРЕНА ОНА ОКРУГЛЯЕТСЯ - неявная особенность Interbase или ГЛЮК - а может я чего не знаю где-то устанавливается способ округления чисел. С числами приведенными в первом примере можете сами проверить - очень наглядно!!!
пример в SQL
Функцию R_RountTo можно брать любую - все равно результат меняется до нее, я сейчас использую не стандартную делфи, а свою которая округляет в большую сторону если 5 и в меньшую если до 5:
Фрагмент процедуры SQL:
as
ReturnValue DoublePrecision;
begin
...
ReturnValue = R_RountTo(20.8335, -3)...
...
end
/* так все работает в ReturnValue будет 20.834*/
as
Value Numeric(15,10);
ReturnValue DoublePrecision;
begin
...
Value = 20.8335;
ReturnValue = R_RountTo(Value, -3)...
...
end
/* так тоже в ReturnValue будет 20.834*/
as
Value DoublePrecision;
ReturnValue DoublePrecision;
begin
...
Value = 20.8335;
ReturnValue = R_RountTo(Value, -3)...
...
end
/* так не работает !!!! ReturnValue будет 20.833 - причем это значение уже на входе процедуры UDF */
Фрагмент процедуры SQL:
as
ReturnValue DoublePrecision;
begin
...
ReturnValue = R_RountTo(20.8335, -3)...
...
end
/* так все работает в ReturnValue будет 20.834*/
as
Value Numeric(15,10);
ReturnValue DoublePrecision;
begin
...
Value = 20.8335;
ReturnValue = R_RountTo(Value, -3)...
...
end
/* так тоже в ReturnValue будет 20.834*/
as
Value DoublePrecision;
ReturnValue DoublePrecision;
begin
...
Value = 20.8335;
ReturnValue = R_RountTo(Value, -3)...
...
end
/* так не работает !!!! ReturnValue будет 20.833 - причем это значение уже на входе процедуры UDF */
как раз в том члучае который вы процетировали - все работает, а вот если использовать Double Precision - то не работает, я же подробно 3 примера описал, кстати если использовать Numeric(15, 10) - то никакого преобразования не происходит - точнее оно касается только 11 знака после запятой - а именно число округляется до 10 знака, СОВЕРШЕННО ТОЧНО что с Numeric(15, 10) все ОК, а вот Double Precision - не раб, что меня и удивило - изначально у меня в процедурах вообще не было Numeric(15, 10) - это уже после выявления непонятностей с округлением, так что мне нечего было мешать, или что нельзя мешать Double Precision - c целыми константами?, разве результирующее выражение не должно быть Double Precision? (... Value Double Precision ... Ret = Value * 1.5 *1.01)
Но даже если не мешать не работает даже если просто передавать переменную Double Precision причем только в ХП. Например если в IBExpert в построитель запросов передавать след SQL:
/***/
select r_roundto(20.8335, 3)
from ANY_TABLE
where 1 = 1
/***/
/***/
select r_roundto(cast(20.8335 as double precision), 3)
from ANY_TABLE
where 1 = 1
/***/
/***/
select r_roundto(cast(20.8335 as numeric(15,10, 3)
from ANY_TABLE
where 1 = 1
/***/
то во всех случаях ВСЕ РАБОТАЕТ htpekmnfn 20.834
Но даже если не мешать не работает даже если просто передавать переменную Double Precision причем только в ХП. Например если в IBExpert в построитель запросов передавать след SQL:
/***/
select r_roundto(20.8335, 3)
from ANY_TABLE
where 1 = 1
/***/
/***/
select r_roundto(cast(20.8335 as double precision), 3)
from ANY_TABLE
where 1 = 1
/***/
/***/
select r_roundto(cast(20.8335 as numeric(15,10, 3)
from ANY_TABLE
where 1 = 1
/***/
то во всех случаях ВСЕ РАБОТАЕТ htpekmnfn 20.834
Нет, никто не проверяет. Потому что
а) все знают разницу между числами и арифметиками с плавающей и фиксированной точками, а также в чём разница округления по Гауссу и по церковно-приходской школе и какое выполняет сопроцессор.
б) никто не знает (и не интересуется) как и что ты округляешь в своей УДФ, тыкая ей на вход разные типы и включая этим неявные преобразования типов IB/FB, порядок выполнения которых зависит от порядка следования операндов в выражении и действующие по-разному для чисел с фиксированной и плавающей точкой.
Для общего развития очень рекомендую
http://www.delphikingdom.com/asp/viewit ... /reals.htm
а) все знают разницу между числами и арифметиками с плавающей и фиксированной точками, а также в чём разница округления по Гауссу и по церковно-приходской школе и какое выполняет сопроцессор.
б) никто не знает (и не интересуется) как и что ты округляешь в своей УДФ, тыкая ей на вход разные типы и включая этим неявные преобразования типов IB/FB, порядок выполнения которых зависит от порядка следования операндов в выражении и действующие по-разному для чисел с фиксированной и плавающей точкой.
Для общего развития очень рекомендую
http://www.delphikingdom.com/asp/viewit ... /reals.htm
проверяю. использую round из rfunc.Так я не пойму примеры данные в Посте кто-то проверяет
Код: Выделить всё
CREATE PROCEDURE A
RETURNS (
RETVAL DOUBLE PRECISION)
AS
DECLARE variable val double precision;
begin
val=20.8335;
retval=round(val, 3);
suspend;
end
если вместо retval = round
написать retval = val, вернет 20.8335.
я правильно проверил? FB 1.5.2
и опять же.
почему нельзя написать
R_RoundTo(var AValue: double; var aDigit: PInteger).
? чего это за выкрутасы с приемом каких-то указателей... по человечески нельзя?
функция принимает double. объявлена наверняка как double. Но - ты в нее передаешь и double и numeric. Зачем? Это, как бы, повтор вопроса про смешивание целочисленной и вещественной арифметики.Если при передаче параметров тип переменной отличный от Numeric, то округление происходит неверно
"я худею от радио ..."function R_RoundTo(aValue: PDouble; aDigit: PInteger): Double; cdecl; export;
почему нельзя написать
R_RoundTo(var AValue: double; var aDigit: PInteger).
? чего это за выкрутасы с приемом каких-то указателей... по человечески нельзя?
!!!
Во первых огромное спасибо за то что помогли разобраться, оказывается в Interbase имеет значение порядок множителей - то есть влияет на конечный результат. Как именно происходит преобразование типов в зависимости от порядка множителей - например в следующей процедуре:
CREATE PROCEDURE A
RETURNS (
QNT_NORM DOUBLE PRECISION,
QNT_PRODUCT DOUBLE PRECISION,
RAW_PERCENT DOUBLE PRECISION)
AS
begin
RAW_PERCENT = 4.1667;
QNT_PRODUCT = 500;
QNT_NORM = QNT_PRODUCT * RAW_PERCENT * 0.01;
/* QNT_NORM = 0.01 * QNT_PRODUCT * RAW_PERCENT;*/
/*в этой строке после перестановки множителей - результат 20.833*/
QNT_NORM = r_roundto(QNT_NORM, 3);
suspend;
end
Почему - именно после передачи параметров в UDF - происходит преобразование, ведь если не вызывать UDF - в обоих случаях результат будет одинаков. (20.8335) - Видимо меняется какой то бит - воторый означает конечный тип числа?
И еще - могу ли я вместо UDF для округления использовать присваивание из Double Precision в Numeric (в Numeric(15,3) - для округления до тысячных) - значение при этом округляется правильно, есть ли какие то особенности?
CREATE PROCEDURE A
RETURNS (
QNT_NORM DOUBLE PRECISION,
QNT_PRODUCT DOUBLE PRECISION,
RAW_PERCENT DOUBLE PRECISION)
AS
begin
RAW_PERCENT = 4.1667;
QNT_PRODUCT = 500;
QNT_NORM = QNT_PRODUCT * RAW_PERCENT * 0.01;
/* QNT_NORM = 0.01 * QNT_PRODUCT * RAW_PERCENT;*/
/*в этой строке после перестановки множителей - результат 20.833*/
QNT_NORM = r_roundto(QNT_NORM, 3);
suspend;
end
Почему - именно после передачи параметров в UDF - происходит преобразование, ведь если не вызывать UDF - в обоих случаях результат будет одинаков. (20.8335) - Видимо меняется какой то бит - воторый означает конечный тип числа?
И еще - могу ли я вместо UDF для округления использовать присваивание из Double Precision в Numeric (в Numeric(15,3) - для округления до тысячных) - значение при этом округляется правильно, есть ли какие то особенности?
если работать, то используй или только double, или только numeric, и не парь мозги. про особенности вещественных чисел тебе сказали, дали ссылки, да и на сайте есть нечто вроде статьи. Про numeric тоже есть английская статья, где преобразования тоже описаны. все в разделе "статьи".Очень интересно - мне же с этим работать