# -*- coding: utf-8 -*-
#author : wayne http://blog.csdn.net/wayne92/
#email:moonbingbing@gmail.com
import time
def decimal_to_fraction(value,radix):
'''
把小数转换为分数。
方法:比如输入0.21,radix为2.那么0.21=21/100,因为要求分母为
2位,所以分母要变小,所以分子也要随着变小。分子的范围就是[1,21].
根据每个分子的不同,int(分母)=分子/0.23, 再由分子/分母得出一个近似值。
只要找出误差最小的那个就可以了。
Args:
value:小数的值。
radix:分母的位数。
Returns:
返回tuple:(正负号,整数部分,分子,分母,
(分子/分母)的值,(分子/分母)表示的循环小数,误差)
'''
#if str(value) == repr(value):
# return None
if value < 0: #把负数转换为正数
sign = '-'
value = abs(value)
else:
sign = '+'
base = 10**radix
integer = int(str(value).split('.')[0]) #整数部分
value = value - integer #小数部分
min_error = 1 #最小误差
best = None #返回tuple
circulator = 0 #得到分数的循环小数
int_numerator = int(value*base) #int_numerator为分子的最大值
for numerator in range(int_numerator,0,-1):
result = numerator / value
denominator = int(result+0.5)
if denominator == base:
continue
my_value = float(numerator)/denominator
error = abs(my_value - value)
if min_error > error:
min_error = error
circulator = fraction_to_circulator(numerator,denominator)
best = (sign,integer,numerator,denominator,my_value,circulator,min_error)
if min_error == 0: #找到第一个误差为0的值,就退出.如果不这样处理,效率会低很多。
break
return best
def fraction_to_circulator(dividend, divisor, precision=1000):
'''
把分数转换为循环小数。
理论依据:任何分数都可以转换为有限小数或者无限循环小数。
Args:
dividend: 被除数
divisor: 除数
precision:精度。即计算循环小数时,最多计算到的小数点后多少位
Returns:
循环小数(字符串)。格式为:1.2(34)。括号中的是循环体。
'''
pos = 0
modDict = {}
frac = []
is_circulator = False #是否存在循环
div, mod= divmod(dividend, divisor)
intPart = str(div) + '.'
#模拟手算的过程,发现有相同的余数就说明发现循环
while pos < precision and mod != 0 :
modDict[mod] = pos
mod *= 10
div, mod = divmod (mod, divisor)
frac.append(str(div))
if mod in modDict.keys():
is_circulator = True
break
pos += 1
if is_circulator: # 发现循环
frac.insert(modDict[mod],'(')
frac.append(')')
return intPart + ''.join(frac)
def circulator_to_fraction(circulator):
'''
把循环小数转换为分数。
理论依据:每个循环小数都可以很精确的用一个分数来表示。
方法:比如输入0.23(45).先记录循环体的位置,再转换为0.2345。
0.2345... × 100 = 23.45... (1)
0.2345... × 10000 = 2345.45... (2) #这里100和10000是算出来的,不是乱写的
用(2) - (1),得:
0.2345... * 9900 = 2345 - 23 = 2322
所以0.2345... 的分数表示就是 2322/9900.
Args:
circulator:循环小数(字符串)。格式为:1.2(34)。括号中的是循环体。
Returns:
如果格式输入有错误,显示提示,返回None。
否则返回tuple(分子,分母)。
'''
dot = circulator.find('.')
left_p = circulator.find('(')
rigth_p = circulator.find(')')
if dot == -1 or left_p == -1 or rigth_p == -1:
print u'输入循环小数格式错误'
print u'输入格式为 1.23(45),其中45为循环体'
return None
true_circulator = float(circulator[:left_p] + circulator[left_p+1:-1])
first_shift = left_p-dot-1
second_shift = rigth_p-left_p-1+first_shift
first_base = 10**first_shift
second_base = 10**second_shift
denominator = second_base - first_base #分母
numerator = int(true_circulator*second_base)
- int(true_circulator*first_base) #分子
return (numerator,denominator)
if __name__ == '__main__':
begin = time.time()
print decimal_to_fraction(0.0629226211751451,2)
#print fraction_to_circulator(24,999)
#print circulator_to_fraction('0.78(0234)')
end = time.time()
print 'cost time is %f'%(end - begin)
#author : wayne http://blog.csdn.net/wayne92/
#email:moonbingbing@gmail.com
import time
def decimal_to_fraction(value,radix):
'''
把小数转换为分数。
方法:比如输入0.21,radix为2.那么0.21=21/100,因为要求分母为
2位,所以分母要变小,所以分子也要随着变小。分子的范围就是[1,21].
根据每个分子的不同,int(分母)=分子/0.23, 再由分子/分母得出一个近似值。
只要找出误差最小的那个就可以了。
Args:
value:小数的值。
radix:分母的位数。
Returns:
返回tuple:(正负号,整数部分,分子,分母,
(分子/分母)的值,(分子/分母)表示的循环小数,误差)
'''
#if str(value) == repr(value):
# return None
if value < 0: #把负数转换为正数
sign = '-'
value = abs(value)
else:
sign = '+'
base = 10**radix
integer = int(str(value).split('.')[0]) #整数部分
value = value - integer #小数部分
min_error = 1 #最小误差
best = None #返回tuple
circulator = 0 #得到分数的循环小数
int_numerator = int(value*base) #int_numerator为分子的最大值
for numerator in range(int_numerator,0,-1):
result = numerator / value
denominator = int(result+0.5)
if denominator == base:
continue
my_value = float(numerator)/denominator
error = abs(my_value - value)
if min_error > error:
min_error = error
circulator = fraction_to_circulator(numerator,denominator)
best = (sign,integer,numerator,denominator,my_value,circulator,min_error)
if min_error == 0: #找到第一个误差为0的值,就退出.如果不这样处理,效率会低很多。
break
return best
def fraction_to_circulator(dividend, divisor, precision=1000):
'''
把分数转换为循环小数。
理论依据:任何分数都可以转换为有限小数或者无限循环小数。
Args:
dividend: 被除数
divisor: 除数
precision:精度。即计算循环小数时,最多计算到的小数点后多少位
Returns:
循环小数(字符串)。格式为:1.2(34)。括号中的是循环体。
'''
pos = 0
modDict = {}
frac = []
is_circulator = False #是否存在循环
div, mod= divmod(dividend, divisor)
intPart = str(div) + '.'
#模拟手算的过程,发现有相同的余数就说明发现循环
while pos < precision and mod != 0 :
modDict[mod] = pos
mod *= 10
div, mod = divmod (mod, divisor)
frac.append(str(div))
if mod in modDict.keys():
is_circulator = True
break
pos += 1
if is_circulator: # 发现循环
frac.insert(modDict[mod],'(')
frac.append(')')
return intPart + ''.join(frac)
def circulator_to_fraction(circulator):
'''
把循环小数转换为分数。
理论依据:每个循环小数都可以很精确的用一个分数来表示。
方法:比如输入0.23(45).先记录循环体的位置,再转换为0.2345。
0.2345... × 100 = 23.45... (1)
0.2345... × 10000 = 2345.45... (2) #这里100和10000是算出来的,不是乱写的
用(2) - (1),得:
0.2345... * 9900 = 2345 - 23 = 2322
所以0.2345... 的分数表示就是 2322/9900.
Args:
circulator:循环小数(字符串)。格式为:1.2(34)。括号中的是循环体。
Returns:
如果格式输入有错误,显示提示,返回None。
否则返回tuple(分子,分母)。
'''
dot = circulator.find('.')
left_p = circulator.find('(')
rigth_p = circulator.find(')')
if dot == -1 or left_p == -1 or rigth_p == -1:
print u'输入循环小数格式错误'
print u'输入格式为 1.23(45),其中45为循环体'
return None
true_circulator = float(circulator[:left_p] + circulator[left_p+1:-1])
first_shift = left_p-dot-1
second_shift = rigth_p-left_p-1+first_shift
first_base = 10**first_shift
second_base = 10**second_shift
denominator = second_base - first_base #分母
numerator = int(true_circulator*second_base)
- int(true_circulator*first_base) #分子
return (numerator,denominator)
if __name__ == '__main__':
begin = time.time()
print decimal_to_fraction(0.0629226211751451,2)
#print fraction_to_circulator(24,999)
#print circulator_to_fraction('0.78(0234)')
end = time.time()
print 'cost time is %f'%(end - begin)
利用这种方法实现分数小数的互相转换,发现比ET要准确。ET下一个版本用我的算法吧,呵呵