Зберігання грошей у десятковій колонці - яка точність та масштабність?


173

Я використовую десятковий стовпець для зберігання значень грошей у базі даних, і сьогодні мені було цікаво, яку точність та масштаб використовувати.

Оскільки нібито стовпчики з фіксованою шириною є більш ефективними, я думав, що те ж саме може бути і для десяткових стовпців. Є це?

І яку точність і масштаб я повинен використовувати? Я думав про точність 24/8. Це перенаселення недостатньо чи нормально?


Ось що я вирішив зробити:

  • Зберігайте коефіцієнти конверсії (коли це застосовується) у самій таблиці транзакцій, як плаваючу
  • Зберігайте валюту в таблиці рахунків
  • Сума транзакції буде a DECIMAL(19,4)
  • Усі розрахунки за допомогою коефіцієнта конверсії будуть оброблятися моєю заявою, тому я постійно контролюю питання округлення

Я не думаю, що плаваючий коефіцієнт конверсії не є проблемою, оскільки це здебільшого для довідки, і я все-таки передаваю його до десяткової.

Дякую всім за цінний внесок.


2
Задайте собі питання: чи дійсно потрібно зберігати дані у десятковій формі? Не можу я зберігати дані у вигляді центів / грошей -> цілих чисел?
Теренс

5
DECIMAL(19, 4) це популярний вибір, перевірте це, також перевірте тут Формати світової валюти, щоб визначити, скільки десяткових знаків використовувати, надію, допоможе.
shaijut

Відповіді:


181

Якщо ви шукаєте єдиний розмір, я б припустив DECIMAL(19, 4), що це популярний вибір (швидкий Google вирішує це). Я думаю, що це походить від старого типу даних VBA / Access / Jet Currency, є першим десятковим типом у фіксованій точці на мові; Decimalу VB6 / VBA6 / Jet 4.0 лише у стилі "версія 1.0" (тобто не повністю реалізований).

Правило зберігання десяткових значень з фіксованою точкою полягає в тому, щоб зберігати принаймні ще один десятковий знак, ніж потрібно насправді для округлення. Однією з причин відображення старого Currencyтипу в передній частині для DECIMAL(19, 4)введення в задній частині було те, що Currencyпроявилися закругленість банкірів за своєю природою, тоді як DECIMAL(p, s)округлена усіченням.

Додатковий десяткове місце у сховищі для DECIMALдозволяє реалізувати спеціальний алгоритм округлення, а не приймати постачальника за замовчуванням (а округлення банкірів викликає тривогу, щонайменше, для дизайнера, який очікує, що всі значення, що закінчуються на. .

Так, DECIMAL(24, 8)мені це здається непосильним. Більшість валют котируються у чотири чи п’ять знаків після коми. Я знаю ситуації, коли потрібна десяткова шкала 8 (або більше) , але саме тут пропорційна "нормальна" грошова сума (скажімо, чотири десяткових знаки), що означає, що десяткову точність слід відповідно зменшити (також врахуйте тип плаваючої точки в таких умовах). І ніхто сьогодні не має стільки грошей, щоб вимагати точної десяткової точності 24 :)

Однак, а не підхід одного розміру, деякі дослідження можуть бути в порядку. Попросіть вашого дизайнера або доменну експерта про правила обліку , які можуть бути застосовні: GAAP, ЄС і т.д. Я погано пам'ятаю деякі внутрішньодержавні перекази ЄС з явними правилами округлення до п'яти знаків після коми, тому , використовуючи DECIMAL(p, 6)для зберігання. Як правило, бухгалтери віддають перевагу чотирьом десятковим знакам.


PS Уникайте MONEYтипу даних SQL Server, оскільки він має серйозні проблеми з точністю під час округлення, серед інших міркувань, таких як портативність тощо. Див . Блог Аарона Бертранда .


Майкрософські та мовні дизайнери обрали банкірське округлення, оскільки дизайнери обладнання обрали його [цитування?]. Наприклад, це закріплено в стандартах Інституту інженерів електротехніки та електроніки (IEEE). І дизайнери обладнання обрали його, тому що математики віддають перевагу. Дивіться Вікіпедію ; Якщо перефразовувати: Видання "Вірогідність і теорія помилок" 1906 року називало це "правилом комп'ютера" ("комп'ютери", що означає людей, які виконують обчислення).


1
Дивіться цю відповідь та цю сторінку, щоб дізнатися, чому мовні дизайнери обирають заокруглення Banker.
Нік Чаммас

1
onedaywhen: Заокруглення банкіра не пов'язане з Microsoft. @ NickChammas: і це не винахід дизайнера мови. Майкрософські та мовні дизайнери в основному обрали його, тому що дизайнери обладнання вибрали його; наприклад, це закріплено в стандартах Інституту інженерів електротехніки та електроніки (IEEE), наприклад. І дизайнери обладнання обрали його, тому що математики віддають перевагу. Див. En.wikipedia.org/wiki/Rounding#History ; Якщо перефразовувати: Видання "Вірогідність і теорія помилок" 1906 року називало це "правилом комп'ютера" ("комп'ютери", що означає людей, які виконують обчислення).
фог

9
так що я повинен використовувати для Bitcoin?
Інструментарій

1
@onedaywhen чому DECIMAL(19, 4)популярніший DECIMAL(19, 2)? Більшість світових валют - це лише два знаки після коми.
кокеду

3
@ zypA13510: так, моє твердження так десять років тому! Але це мій третій найбільш проголосований за відповідь і пояснює справедливий шматок змін, що стосується мого репрезентатора SO, тому я є дітьми :)
onedaywhen

105

Нещодавно ми впровадили систему, яка повинна обробляти значення в декількох валютах і конвертувати між ними, а також вирішувала кілька речей важким шляхом.

НІКОЛИ НЕ ВИКОРИСТОВУЮТЬСЯ ПЛАТИВНІ НОМЕРИ НА ГРОШІ

Арифметика з плаваючою точкою вносить неточності, які можуть не помітити, поки вони щось не викрутили. Усі значення повинні зберігатися як цілі чи фіксовані десяткові типи, і якщо ви вирішите використовувати фіксований десятковий тип, то переконайтеся, що ви точно розумієте, що цей тип робить під кришкою (тобто чи використовує він внутрішнє ціле чи плаваючу крапку тип).

Коли вам потрібно зробити обчислення або перетворення:

  1. Перетворити значення у плаваючу точку
  2. Обчисліть нове значення
  3. Заокруглете число і перетворіть його на ціле число

Перетворюючи число плаваючої точки назад в ціле число на кроці 3, не просто додайте його - використовуйте математичну функцію, щоб спочатку її округлити. Зазвичай це буде round, хоча в особливих випадках це може бути floorабо ceil. Знайте різницю і обережно вибирайте.

Збережіть тип числа поряд із значенням

Це може бути не так важливо для вас, якщо ви обробляєте лише одну валюту, але для нас це було важливо при обробці кількох валют. Ми використовували 3-символьний код для такої валюти, як USD, GBP, JPY, EUR тощо.

Залежно від ситуації, можливо, також буде корисно зберігати:

  • Чи є число до або після оподаткування (і яка ставка податку)
  • Чи є число результатом перетворення (і з чого воно було перетворене)

Знайте межі точності чисел, з якими ви маєте справу

Для реальних цінностей ви хочете бути точними, як найменша одиниця валюти. Це означає, що у вас немає цін, менших від цента, копійки, єни, фену тощо. Не зберігайте значення з більшою точністю, ніж це без жодної причини.

Внутрішньо ви можете вирішити справу з меншими значеннями, в цьому випадку це інший тип вартості валюти . Переконайтеся, що ваш код знає, що це, і не змішувати їх. Уникайте використання значень з плаваючою комою навіть тут.


Додавши всі ці правила разом, ми вирішили наступні правила. У запущеному коді валюти зберігаються, використовуючи ціле число для найменшої одиниці.

class Currency {
   String code;       //  eg "USD"
   int value;         //  eg 2500
   boolean converted;
}

class Price {
   Currency grossValue;
   Currency netValue;
   Tax taxRate;
}

У базі даних значення зберігаються як рядок у такому форматі:

USD:2500

Це зберігає вартість 25,00 доларів. Ми змогли це зробити лише тому, що код, який має справу з валютами, не повинен знаходитися в самому шарі бази даних, тому всі значення спочатку можуть бути перетворені в пам'ять. Інші ситуації, без сумніву, піддаються іншим рішенням.


І якщо я раніше не зрозумів, не використовуйте флоат!


1
Ніколи не кажіть ніколи: іноді грошові суми пропорційно і їх потрібно поповнювати пізніше. Приклад: поділ загального дивіденду (відносно невеликий), розподілений на кількість випущених акцій (порівняно невеликий), щоб отримати чисту на акцію. Іноді пливуть круглі краще :)
день, коли

23
Я стою біля свого ніколи. Специфікація з плаваючою точкою має неточності, які підсумують більше обчислень, які ви робите. Якщо вам потрібно зберігати значення менші, ніж цент та копійки, то визначте необхідний рівень точності та дотримуйтесь цього. Не використовуйте поплавок. Серйозно. Це погана ідея.
Маркус Даунінг

4
Ця відповідь також узгоджується з найкращими практиками JavaScript, викладеними Дугласом Крокфордом у його серії "Крокфорд на JavaScript", де він рекомендує робити всі розрахунки валюти в ПЕННІ, щоб уникнути помилок машин при округленні. Отже, якщо ви працюєте з валютами в javascript, то має велике сенс зберігати значення таким чином.
paperreduction

1
@onedaywhen Ці чисті випадки на одну акцію, ви можете підсумувати пропорційні суми та порівняти їх з початковою сумою та розробити стратегію поводження з рештою (при використанні типів десяткових / цілих чисел).
Шив

2
Для Mysql я рекомендую зберігати ціле число (2500) як а, bigintякщо ви плануєте сортувати за сумою. І не витрачайте час на 32-бітний PHP, працюючи з великими цілими числами, оновіть до 64 біт або Node.JS;)
Ricky Boyce

4

Обробляючи гроші в MySQL, використовуйте DECIMAL (13,2), якщо ви знаєте точність ваших грошових значень, або використовуйте DOUBLE, якщо ви просто хочете швидко отримати досить приблизне значення. Отже, якщо вашій програмі потрібно обробляти кошти до трильйона доларів (або євро або фунтів), тоді це має спрацювати:

DECIMAL(13, 2)

Або, якщо вам потрібно дотримуватися GAAP, тоді використовуйте:

DECIMAL(13, 4)

3
Чи можете ви зв’язати певну частину вказівок GAAP замість вмісту документа на 2500 сторінок? Дякую.
ReactingToAngularVues

@ReactingToAngularVues здається, що сторінка змінилася. Вибачте
pollux1er

2

4 десяткових знаки дадуть точність зберігання найменших валютних підрозділів у світі. Ви можете зняти його далі, якщо вам потрібна точність мікроплати (наноплати ?!).

Я теж віддаю перевагу DECIMAL специфічним для СУБД видів грошей, ви безпечніше зберігати таку логіку в застосуванні IMO. Інший підхід у цих же рядках - просто використовувати [довге] ціле число з форматуванням у ¤unit.subunit для людської читабельності (¤ = символ валюти), що робиться на рівні програми.


1

Тип даних грошей на SQL Server має чотири цифри після десяткової.

З онлайн-книг SQL Server 2000:

Грошові дані представляють позитивні чи негативні суми грошей. У Microsoft® SQL Server ™ 2000 грошові дані зберігаються з використанням даних про гроші та малі гроші. Грошові дані можна зберігати з точністю до чотирьох знаків після коми. Використовуйте тип даних про гроші, щоб зберігати значення в діапазоні від -922,337,203,685,477,5808 до +922,337,203,685,477,5807 (для збереження значення потрібно 8 байт). Використовуйте тип даних smallmoney, щоб зберігати значення в діапазоні від -214,748.3648 до 214,748.3647 (для збереження значення потрібно 4 байти). Якщо потрібна більша кількість десяткових знаків, скористайтеся замість цього десяткового типу даних.


1

Іноді вам потрібно буде перейти на менше ніж цент, і є міжнародна валюта, яка використовує дуже великі демонації. Наприклад, ви можете стягувати з клієнтів 0,088 копійок за транзакцію. У моїй базі даних Oracle стовпці визначаються як НОМЕР (20,4)


1

Якщо ви збираєтесь робити якісь арифметичні операції в БД (помножуючи тарифні платежі тощо), ви, мабуть, захочете набагато більшої точності, ніж тут пропонують люди, з тих самих причин, що ви ніколи хочете використовувати що-небудь менше, ніж значення подвійної точності з плаваючою комою в коді програми.


Про це я і думав, але з точки зору валютних курсів (тобто перерахунку доларів Зімбаве в долари США). Я проведу кілька експериментів над базами даних, які я використовую (psql, sqlite), щоб побачити, як вони обробляють округлення на дуже маленьких десяткових колах.
Іван

Крім того, чи не мають плавці проблеми з точністю в деяких dbms / languages?
Іван

2
floats мають проблеми з точністю на ВСІХ мовах.
Маркус Даунінг

Найпоширеніша рекомендація в наші дні - використовувати довільну точність (думати BigDecimal), але тривалий час це була подвійна точність (думайте doubleзамість float). Крім того, довільна точність має значні штрафні санкції в деяких випадках. Тестування, безумовно, є правильним підходом.
Хенк Гей

0

Якщо ви використовували динамічний сервер IBM Informix, у вас буде тип MONEY, який є другорядним варіантом для типу DECIMAL або NUMERIC. Це завжди тип фіксованої точки (тоді як DECIMAL може бути типом з плаваючою точкою). Можна вказати шкалу від 1 до 32, а точність від 0 до 32 (за замовчуванням до шкали 16 і точності 2). Отже, залежно від того, що потрібно зберігати, ви можете використовувати DECIMAL (16,2) - все ще достатньо великий, щоб утримувати Федеральний дефіцит США до найближчого цента - або ви можете використовувати менший діапазон чи більше десяткових знаків.


0

Я думаю, що велика частина ваших вимог або вашого клієнта повинна диктувати, яку точність і масштаб використовувати. Наприклад, для веб-сайту електронної комерції, над якою я працюю над грошима лише в GBP, від мене вимагали дотримуватися знаків десяткової (6, 2).


0

Тут пізня відповідь, але я використав

DECIMAL(13,2)

що я маю рацію на думці, має дозволити до 99,999,999,999,99.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.