Сообщение Re[9]: Числа с плавающей точкой от 10.04.2016 9:28
Изменено 11.04.2016 4:39 netch80
Здравствуйте, ·, Вы писали:
·>Да, в спеках UK-налоговой там шесть видов округлений: до пенсов, до фунтов * вверх, вниз, банковское.
·>Вопрос — возможно ли реализовать эти округления используя тип double?
Любое округление до доли размером P делается в случае идеально точных расчётов по формуле P*ROUND(val/P), где val — исходное значение, ROUND — конкретная функция округления до целого.
Как называется эта функция — зависит от языка.
В случае C: round() округляет до ближайшего, а половинки — в направлении от нуля; nearbyint() — в соответствии с текущим направлением (по умолчанию это к ближайшему и банкирское); rint() — то же, что nearbyint(), но поднимая inexact (для данной задачи явно не нужно); floor(), ceil() — любые с ненулевой целой частью. trunc() аналогично floor() для положительных и ceil() для отрицательных, отдельно не вспоминаем. Также nearbyint() при FE_DOWNWARD аналогично floor(), а при FE_TOWARD — ceil().
Пример: входное значение и результат округления; NB/RNE — nearbyint() при округлении к ближайшему (умолчание FE_TONEAREST для IEEE).
Если сумма в double в фунтах, то соответственно получается (но см. уточнение ниже):
для фунтов — ROUND(val); для пенсов — ROUND(val*100)/100.
Вверх — ROUND(x) = ceil(x); вниз — ROUND(x) = floor(x). Банковское — только NB/RNE.
Тут, однако, есть одна тонкость. Представим себе, что в результате некоторой операции у нас получилась сумма в фунтах вида 12.0299999999999999. Она должна восприниматься как 12.03 перед всеми воздействиями! Оба округления вверх и вниз до центов должны дать 12.03, хотя внутри это снова может оказаться 12.0299999999999999 или 12.03000000000002. Ещё хуже, если мы попытаемся округлять какое-нибудь 11.99999999999999 вниз до целых фунтов, получив 11, когда на самом деле нужно 12.
Поэтому: обычно говорят, что многократное округление — это очень вредно, приводя пример ситуацйй вида "1.453 сначала округлили до сотых до 1.45, а потом до десятых банкирским до 1.4; сразу до десятых это дало бы более корректное 1.5"; аналогично при правиле типа "0.5 округляется вверх" 1.47->1.5->2 вместо 1.47->1". Но в случае финансов, наоборот, необходимо двойное округление! (Но не более) 11.99999999999999 до фунтов — сначала должно быть округлено до центов — до 12.00, а потом уже смотреть, куда его и как округлять; иначе округление вниз до целых фунтов даст 11, а не правильное 12. А так как округление до центов это nearbyint(x*100)/100, то может оказаться, что лучше провести в целых всю цепочку округления, а не одну операцию (а то и вообще перейти на fixed).
·>Да, в спеках UK-налоговой там шесть видов округлений: до пенсов, до фунтов * вверх, вниз, банковское.
·>Вопрос — возможно ли реализовать эти округления используя тип double?
Любое округление до доли размером P делается в случае идеально точных расчётов по формуле P*ROUND(val/P), где val — исходное значение, ROUND — конкретная функция округления до целого.
Как называется эта функция — зависит от языка.
В случае C: round() округляет до ближайшего, а половинки — в направлении от нуля; nearbyint() — в соответствии с текущим направлением (по умолчанию это к ближайшему и банкирское); rint() — то же, что nearbyint(), но поднимая inexact (для данной задачи явно не нужно); floor(), ceil() — любые с ненулевой целой частью. trunc() аналогично floor() для положительных и ceil() для отрицательных, отдельно не вспоминаем. Также nearbyint() при FE_DOWNWARD аналогично floor(), а при FE_TOWARD — ceil().
Пример: входное значение и результат округления; NB/RNE — nearbyint() при округлении к ближайшему (умолчание FE_TONEAREST для IEEE).
x floor(x) ceil(x) round(x) NB/RNE
2.0 2.0 2.0 2.0 2.0
2.2 2.0 3.0 2.0 2.0
2.5 2.0 3.0 3.0 2.0
2.8 2.0 3.0 3.0 3.0
3.0 3.0 3.0 3.0 3.0
3.2 3.0 4.0 3.0 3.0
3.5 3.0 4.0 4.0 4.0
3.8 3.0 4.0 4.0 4.0
4.0 4.0 4.0 4.0 4.0
Если сумма в double в фунтах, то соответственно получается (но см. уточнение ниже):
для фунтов — ROUND(val); для пенсов — ROUND(val*100)/100.
Вверх — ROUND(x) = ceil(x); вниз — ROUND(x) = floor(x). Банковское — только NB/RNE.
Тут, однако, есть одна тонкость. Представим себе, что в результате некоторой операции у нас получилась сумма в фунтах вида 12.0299999999999999. Она должна восприниматься как 12.03 перед всеми воздействиями! Оба округления вверх и вниз до центов должны дать 12.03, хотя внутри это снова может оказаться 12.0299999999999999 или 12.03000000000002. Ещё хуже, если мы попытаемся округлять какое-нибудь 11.99999999999999 вниз до целых фунтов, получив 11, когда на самом деле нужно 12.
Поэтому: обычно говорят, что многократное округление — это очень вредно, приводя пример ситуацйй вида "1.453 сначала округлили до сотых до 1.45, а потом до десятых банкирским до 1.4; сразу до десятых это дало бы более корректное 1.5"; аналогично при правиле типа "0.5 округляется вверх" 1.47->1.5->2 вместо 1.47->1". Но в случае финансов, наоборот, необходимо двойное округление! (Но не более) 11.99999999999999 до фунтов — сначала должно быть округлено до центов — до 12.00, а потом уже смотреть, куда его и как округлять; иначе округление вниз до целых фунтов даст 11, а не правильное 12. А так как округление до центов это nearbyint(x*100)/100, то может оказаться, что лучше провести в целых всю цепочку округления, а не одну операцию (а то и вообще перейти на fixed).
Re[9]: Числа с плавающей точкой
Здравствуйте, ·, Вы писали:
·>Да, в спеках UK-налоговой там шесть видов округлений: до пенсов, до фунтов * вверх, вниз, банковское.
·>Вопрос — возможно ли реализовать эти округления используя тип double?
Любое округление до доли размером P делается в случае идеально точных расчётов по формуле P*ROUND(val/P), где val — исходное значение, ROUND — конкретная функция округления до целого.
Как называется эта функция — зависит от языка.
В случае C: round() округляет до ближайшего, а половинки — в направлении от нуля; nearbyint() — в соответствии с текущим направлением (по умолчанию это к ближайшему и банкирское); rint() — то же, что nearbyint(), но поднимая inexact (для данной задачи явно не нужно); floor(), ceil() — любые с ненулевой целой частью. trunc() аналогично floor() для положительных и ceil() для отрицательных, отдельно не вспоминаем. Также nearbyint() при FE_DOWNWARD аналогично floor(), а при FE_UPWARD — ceil().
Пример: входное значение и результат округления; NB/RNE — nearbyint() при округлении к ближайшему (умолчание FE_TONEAREST для IEEE).
Если сумма в double в фунтах, то соответственно получается (но см. уточнение ниже):
для фунтов — ROUND(val); для пенсов — ROUND(val*100)/100.
Вверх — ROUND(x) = ceil(x); вниз — ROUND(x) = floor(x). Банковское — только NB/RNE.
Тут, однако, есть одна тонкость. Представим себе, что в результате некоторой операции у нас получилась сумма в фунтах вида 12.0299999999999999. Она должна восприниматься как 12.03 перед всеми воздействиями! Оба округления вверх и вниз до центов должны дать 12.03, хотя внутри это снова может оказаться 12.0299999999999999 или 12.03000000000002. Ещё хуже, если мы попытаемся округлять какое-нибудь 11.99999999999999 вниз до целых фунтов, получив 11, когда на самом деле нужно 12.
Поэтому: обычно говорят, что многократное округление — это очень вредно, приводя пример ситуацйй вида "1.453 сначала округлили до сотых до 1.45, а потом до десятых банкирским до 1.4; сразу до десятых это дало бы более корректное 1.5"; аналогично при правиле типа "0.5 округляется вверх" 1.47->1.5->2 вместо 1.47->1". Но в случае финансов, наоборот, необходимо двойное округление! (Но не более) 11.99999999999999 до фунтов — сначала должно быть округлено до центов — до 12.00, а потом уже смотреть, куда его и как округлять; иначе округление вниз до целых фунтов даст 11, а не правильное 12. А так как округление до центов это nearbyint(x*100)/100, то может оказаться, что лучше провести в целых всю цепочку округления, а не одну операцию (а то и вообще перейти на fixed).
·>Да, в спеках UK-налоговой там шесть видов округлений: до пенсов, до фунтов * вверх, вниз, банковское.
·>Вопрос — возможно ли реализовать эти округления используя тип double?
Любое округление до доли размером P делается в случае идеально точных расчётов по формуле P*ROUND(val/P), где val — исходное значение, ROUND — конкретная функция округления до целого.
Как называется эта функция — зависит от языка.
В случае C: round() округляет до ближайшего, а половинки — в направлении от нуля; nearbyint() — в соответствии с текущим направлением (по умолчанию это к ближайшему и банкирское); rint() — то же, что nearbyint(), но поднимая inexact (для данной задачи явно не нужно); floor(), ceil() — любые с ненулевой целой частью. trunc() аналогично floor() для положительных и ceil() для отрицательных, отдельно не вспоминаем. Также nearbyint() при FE_DOWNWARD аналогично floor(), а при FE_UPWARD — ceil().
Пример: входное значение и результат округления; NB/RNE — nearbyint() при округлении к ближайшему (умолчание FE_TONEAREST для IEEE).
x floor(x) ceil(x) round(x) NB/RNE
2.0 2.0 2.0 2.0 2.0
2.2 2.0 3.0 2.0 2.0
2.5 2.0 3.0 3.0 2.0
2.8 2.0 3.0 3.0 3.0
3.0 3.0 3.0 3.0 3.0
3.2 3.0 4.0 3.0 3.0
3.5 3.0 4.0 4.0 4.0
3.8 3.0 4.0 4.0 4.0
4.0 4.0 4.0 4.0 4.0
Если сумма в double в фунтах, то соответственно получается (но см. уточнение ниже):
для фунтов — ROUND(val); для пенсов — ROUND(val*100)/100.
Вверх — ROUND(x) = ceil(x); вниз — ROUND(x) = floor(x). Банковское — только NB/RNE.
Тут, однако, есть одна тонкость. Представим себе, что в результате некоторой операции у нас получилась сумма в фунтах вида 12.0299999999999999. Она должна восприниматься как 12.03 перед всеми воздействиями! Оба округления вверх и вниз до центов должны дать 12.03, хотя внутри это снова может оказаться 12.0299999999999999 или 12.03000000000002. Ещё хуже, если мы попытаемся округлять какое-нибудь 11.99999999999999 вниз до целых фунтов, получив 11, когда на самом деле нужно 12.
Поэтому: обычно говорят, что многократное округление — это очень вредно, приводя пример ситуацйй вида "1.453 сначала округлили до сотых до 1.45, а потом до десятых банкирским до 1.4; сразу до десятых это дало бы более корректное 1.5"; аналогично при правиле типа "0.5 округляется вверх" 1.47->1.5->2 вместо 1.47->1". Но в случае финансов, наоборот, необходимо двойное округление! (Но не более) 11.99999999999999 до фунтов — сначала должно быть округлено до центов — до 12.00, а потом уже смотреть, куда его и как округлять; иначе округление вниз до целых фунтов даст 11, а не правильное 12. А так как округление до центов это nearbyint(x*100)/100, то может оказаться, что лучше провести в целых всю цепочку округления, а не одну операцию (а то и вообще перейти на fixed).