প্রথমত, ফাংশন, যারা কেবল কিছু অনুলিপি এবং পেস্ট কোড চান:
def truncate(f, n):
'''Truncates/pads a float f to n decimal places without rounding'''
s = '{}'.format(f)
if 'e' in s or 'E' in s:
return '{0:.{1}f}'.format(f, n)
i, p, d = s.partition('.')
return '.'.join([i, (d+'0'*n)[:n]])
এটি পাইথন 2.7 এবং 3.1+ এ বৈধ। পুরানো সংস্করণগুলির জন্য, একই "বুদ্ধিমান রাউন্ডিং" প্রভাব পাওয়া সম্ভব নয় (কমপক্ষে, অনেক জটিল কোড ছাড়াই নয়), তবে কাটা কাটার আগে 12 দশমিক স্থানে গোল করা বেশিরভাগ সময় কাজ করবে:
def truncate(f, n):
'''Truncates/pads a float f to n decimal places without rounding'''
s = '%.12f' % f
i, p, d = s.partition('.')
return '.'.join([i, (d+'0'*n)[:n]])
ব্যাখ্যা
অন্তর্নিহিত পদ্ধতির মূলটি হ'ল মানটিকে নির্ভুলতার সাথে স্ট্রিংয়ে রূপান্তর করা এবং তারপরে কাঙ্ক্ষিত সংখ্যার অক্ষর ছাড়িয়ে সমস্ত কিছু কেটে ফেলা। পরবর্তী পদক্ষেপটি সহজ; স্ট্রিং ম্যানিপুলেশন দিয়ে এটি করা যেতে পারে
i, p, d = s.partition('.')
'.'.join([i, (d+'0'*n)[:n]])
বা decimal
মডিউল
str(Decimal(s).quantize(Decimal((0, (1,), -n)), rounding=ROUND_DOWN))
প্রথম পদক্ষেপটি একটি স্ট্রিংয়ে রূপান্তর করা বেশ কঠিন কারণ এখানে কিছু জোড়া ভাসমান পয়েন্ট আক্ষরিক (যেমন আপনি উত্স কোডটিতে যা লেখেন) উভয়ই একই বাইনারি উপস্থাপনা তৈরি করে এবং তবুও আলাদাভাবে কাটা উচিত। উদাহরণস্বরূপ, 0.3 এবং 0.29999999999999998 বিবেচনা করুন। আপনি যদি 0.3
পাইথন প্রোগ্রামে লিখেন , সংকলকটি আইইইইই ফ্লোটিং-পয়েন্ট ফর্ম্যাটটি বিটগুলির ক্রম অনুসারে এনকোড করে (64৪-বিট ফ্লোট ধরে)
0011111111010011001100110011001100110011001100110011001100110011
এটি 0.3 এর নিকটতম মান যা সঠিকভাবে কোনও আইইইই ফ্লোট হিসাবে প্রতিনিধিত্ব করতে পারে। তবে আপনি যদি 0.29999999999999998
পাইথন প্রোগ্রামে লিখেন , সংকলক একে একে একে একই মান হিসাবে অনুবাদ করবে । একটি ক্ষেত্রে আপনি বোঝাতে চেয়েছিলেন যে এটি কেটে নেওয়া হয়েছে (এক অঙ্কে) 0.3
, অন্য ক্ষেত্রে আপনি বোঝাতে চেয়েছিলেন এটি কেটে ফেলা হয়েছে 0.2
তবে পাইথন কেবল একটি উত্তর দিতে পারে। এটি পাইথনের একটি মৌলিক সীমাবদ্ধতা, বা অলস মূল্যায়ন ছাড়া সত্যই কোনও প্রোগ্রামিং ভাষা। কাটা ফাংশনটির কেবলমাত্র কম্পিউটারের মেমোরিতে সংরক্ষিত বাইনারি মানটিতে অ্যাক্সেস থাকে, আপনি সোর্স কোডে টাইপ করেন এমন স্ট্রিং নয়। 1
আপনি যদি বিটের ক্রমটিকে দশমিক সংখ্যায় আবার ডিকোড করেন, আবার আইইইই 64৪-বিট ফ্লোটিং-পয়েন্ট ফর্ম্যাট ব্যবহার করে আপনি পান
0.2999999999999999888977697537484345957637...
সুতরাং একটি নিরপেক্ষ বাস্তবায়ন উপস্থিত হবে 0.2
যদিও এটি সম্ভবত আপনি চান তা নয়। ভাসমান-পয়েন্ট উপস্থাপনা ত্রুটি সম্পর্কে আরও জানতে পাইথন টিউটোরিয়ালটি দেখুন ।
একটি ভাসমান-পয়েন্ট মানের সাথে কাজ করা খুব বিরল যা কোনও গোলাকার সংখ্যার খুব কাছাকাছি এবং তবুও ইচ্ছাকৃতভাবে round বৃত্তাকার সংখ্যার সমান নয়। সুতরাং সংক্ষিপ্তকরণ করার সময়, মেমরির মানটির সাথে সামঞ্জস্য হতে পারে এমন সমস্তগুলির মধ্যে "সেরা" দশমিক প্রতিনিধিত্ব চয়ন করা বোধগম্য হয়। পাইথন ২.7 এবং তার উপরে (তবে 3.0.০ নয়) কেবলমাত্র এটি করার জন্য একটি পরিশীলিত অ্যালগরিদম অন্তর্ভুক্ত রয়েছে , যা আমরা ডিফল্ট স্ট্রিং বিন্যাসকরণ ক্রিয়াকলাপের মাধ্যমে অ্যাক্সেস করতে পারি।
'{}'.format(f)
কেবলমাত্র সতর্কতাই হ'ল এটি একটি g
ফর্ম্যাট স্পেসিফিকেশনের মতো কাজ করে, এই 1.23e+4
সংখ্যায় যে এটি সংখ্যাটি বড় বা যথেষ্ট ছোট হলে এটি সূচকীয় স্বরলিপি ব্যবহার করে ( )। সুতরাং পদ্ধতিতে এই কেসটি ধরতে হবে এবং এটি অন্যরকমভাবে পরিচালনা করতে হবে। কয়েকটি ক্ষেত্রে রয়েছে যেখানে f
পরিবর্তে ফর্ম্যাট স্পেসিফিকেশন ব্যবহার করা সমস্যার কারণ হয়ে থাকে যেমন 3e-10
২৮ ডিজিটের যথার্থতার সংক্ষিপ্তকরণের চেষ্টা করা (এটি উত্পন্ন করে 0.0000000002999999999999999980
) এবং সেগুলি কীভাবে পরিচালনা করা যায় তা আমি এখনও নিশ্চিত নই।
আপনি যদি আসলে এমনfloat
গুলি নিয়ে কাজ করছেন যা রাউন্ড সংখ্যার খুব নিকটবর্তী তবে ইচ্ছাকৃতভাবে তাদের সমান নয় (যেমন 0.29999999999999998 বা 99.959999999999994), এটি কিছু মিথ্যা ধনাত্মকতা তৈরি করবে, অর্থাত্ এটি গোলাকার সংখ্যা নয় যা আপনি গোল করতে চান নি। সেক্ষেত্রে সমাধানটি একটি নির্দিষ্ট নির্ভুলতা নির্দিষ্ট করে।
'{0:.{1}f}'.format(f, sys.float_info.dig + n + 2)
নির্ভুলতার অঙ্কগুলি এখানে ব্যবহারের পক্ষে সত্যিকার অর্থে কিছু আসে যায় না, স্ট্রিং রূপান্তরটিতে যে কোনও রাউন্ডিং করা হয় তার দশমিক উপস্থাপনার মানটি "বাম্প" না করে তা নিশ্চিত করার জন্য এটি পর্যাপ্ত পরিমাণে বড় হওয়া দরকার। আমি মনে করি sys.float_info.dig + n + 2
সব ক্ষেত্রেই যথেষ্ট হতে পারে, তবে তা না হলে 2
বাড়ানো হতে পারে এবং এটি করতে ক্ষতি হয় না।
পাইথনের পূর্ববর্তী সংস্করণগুলিতে (২.6 বা ৩.০ অবধি) ভাসমান পয়েন্ট সংখ্যা বিন্যাসটি অনেক বেশি অপরিশোধিত ছিল এবং নিয়মিত এগুলি তৈরি করে
>>> 1.1
1.1000000000000001
যদি এটি আপনার পরিস্থিতি হয়, আপনি যদি কাটা কাটা জন্য "সুন্দর" দশমিক উপস্থাপনা ব্যবহার করতে চান, আপনি যা করতে পারেন তা সমস্ত (যতদূর আমি জানি) কিছু সংখ্যক সংখ্যা বেছে নেবে, এটি দ্বারা বর্ণিত পূর্ণ নির্ভুলতার চেয়ে কম float
, এবং গোলাকার এটি কেটে দেওয়ার আগে যে বহু অঙ্কের সংখ্যা। একটি সাধারণ পছন্দ 12,
'%.12f' % f
তবে আপনি যে নম্বরগুলি ব্যবহার করছেন তা অনুসারে এটি সামঞ্জস্য করতে পারেন।
1 ভাল ... আমি মিথ্যা বললাম। প্রযুক্তিগতভাবে, আপনি পাইথনকে তার নিজস্ব উত্স কোডটি পুনরায় পার্স করার নির্দেশ দিতে পারেন এবং কাটা ফাংশনে আপনি প্রথম আর্গুমেন্টের সাথে সংগৃহীত অংশটি বের করতে পারেন। যদি সেই যুক্তিটি একটি ভাসমান-পয়েন্ট আক্ষরিক হয়, আপনি কেবলমাত্র দশমিক পয়েন্টের পরে নির্দিষ্ট সংখ্যক জায়গা কেটে ফেলতে পারেন এবং এটি ফিরে আসতে পারেন। তবে এই কৌশলটি কার্যকর হয় না যদি যুক্তিটি একটি পরিবর্তনশীল হয়, যা এটি মোটামুটি অকেজো করে তোলে। নিম্নলিখিতটি কেবল বিনোদন মূল্যের জন্য উপস্থাপন করা হয়েছে:
def trunc_introspect(f, n):
'''Truncates/pads the float f to n decimal places by looking at the caller's source code'''
current_frame = None
caller_frame = None
s = inspect.stack()
try:
current_frame = s[0]
caller_frame = s[1]
gen = tokenize.tokenize(io.BytesIO(caller_frame[4][caller_frame[5]].encode('utf-8')).readline)
for token_type, token_string, _, _, _ in gen:
if token_type == tokenize.NAME and token_string == current_frame[3]:
next(gen) # left parenthesis
token_type, token_string, _, _, _ = next(gen) # float literal
if token_type == tokenize.NUMBER:
try:
cut_point = token_string.index('.') + n + 1
except ValueError: # no decimal in string
return token_string + '.' + '0' * n
else:
if len(token_string) < cut_point:
token_string += '0' * (cut_point - len(token_string))
return token_string[:cut_point]
else:
raise ValueError('Unable to find floating-point literal (this probably means you called {} with a variable)'.format(current_frame[3]))
break
finally:
del s, current_frame, caller_frame
আপনি যখন কোনও ভেরিয়েবলটি পাস করেন সেই কেসটি হ্যান্ডেল করার জন্য এটি সাধারণকরণটি একটি হারানো কারণের মতো বলে মনে হয়, যেহেতু আপনাকে ভেরিয়েবলের মান দেয় এমন ভাসমান-পয়েন্ট আক্ষরিক না পাওয়া পর্যন্ত আপনাকে প্রোগ্রামের সম্পাদনের মধ্য দিয়ে পিছনের দিকে সন্ধান করতে হবে। এমনকি যদি একটি হয়। বেশিরভাগ ভেরিয়েবলগুলি ব্যবহারকারী ইনপুট বা গাণিতিক এক্সপ্রেশন থেকে শুরু করা হবে, সেক্ষেত্রে বাইনারি প্রতিনিধিত্ব রয়েছে।